/**
 * Fetch B2 Result Step — download a JSON result from Backblaze B2 via S3-compatible presigned URL.
 * Config: {
 *   object_key: "{{context.diagnostic_object_key}}",  // key in the bucket
 *   output_key: "diagnostic_results",                  // context key for parsed JSON
 *   bucket?: "wulf-audits",                            // defaults to B2_BUCKET env
 * }
 */

import crypto from 'crypto';
import { registerStepExecutor } from '../pipeline-engine';
import { PipelineStep, PipelineContext, StepExecutorResult } from '../../types/pipeline';

const B2_DEFAULTS = {
  bucket: process.env.B2_BUCKET || 'wulf-audits',
  region: process.env.B2_REGION || 'us-west-002',
  endpoint: process.env.B2_ENDPOINT || 's3.us-west-002.backblazeb2.com',
  keyId: process.env.B2_KEY_ID || '',
  appKey: process.env.B2_APP_KEY || '',
};

function hmacSha256(key: string | Buffer, data: string): Buffer {
  return crypto.createHmac('sha256', key).update(data).digest();
}

function generatePresignedUrl(objectKey: string, bucket?: string): string {
  const { region, endpoint, keyId, appKey } = B2_DEFAULTS;
  const bkt = bucket || B2_DEFAULTS.bucket;

  if (!keyId || !appKey) {
    throw new Error('B2_KEY_ID and B2_APP_KEY environment variables are required');
  }

  const expiresIn = 600; // 10 minutes
  const method = 'GET';
  const host = endpoint;
  const canonicalUri = `/${bkt}/${objectKey}`;
  const algorithm = 'AWS4-HMAC-SHA256';

  const now = new Date();
  const amzDate = now.toISOString().replace(/[-:]/g, '').split('.')[0] + 'Z';
  const dateStamp = amzDate.slice(0, 8);

  const credentialScope = `${dateStamp}/${region}/s3/aws4_request`;
  const canonicalHeaders = `host:${host}\n`;
  const signedHeaders = 'host';

  const qs: Record<string, string> = {
    'X-Amz-Algorithm': algorithm,
    'X-Amz-Credential': encodeURIComponent(`${keyId}/${credentialScope}`),
    'X-Amz-Date': amzDate,
    'X-Amz-Expires': expiresIn.toString(),
    'X-Amz-SignedHeaders': signedHeaders,
  };

  const canonicalQueryString = Object.keys(qs)
    .sort()
    .map((k) => `${k}=${qs[k]}`)
    .join('&');

  const payloadHash = 'UNSIGNED-PAYLOAD';

  const canonicalRequest = [
    method,
    canonicalUri,
    canonicalQueryString,
    canonicalHeaders,
    signedHeaders,
    payloadHash,
  ].join('\n');

  const stringToSign = [
    algorithm,
    amzDate,
    credentialScope,
    crypto.createHash('sha256').update(canonicalRequest).digest('hex'),
  ].join('\n');

  // Derive signing key
  const kDate = hmacSha256('AWS4' + appKey, dateStamp);
  const kRegion = hmacSha256(kDate, region);
  const kService = hmacSha256(kRegion, 's3');
  const kSigning = hmacSha256(kService, 'aws4_request');

  const signature = crypto
    .createHmac('sha256', kSigning)
    .update(stringToSign)
    .digest('hex');

  return `https://${host}${canonicalUri}?${canonicalQueryString}&X-Amz-Signature=${signature}`;
}

async function executeFetchB2Result(
  step: PipelineStep,
  _context: PipelineContext,
  _executionId: number
): Promise<StepExecutorResult> {
  const objectKey = step.config.object_key;
  const outputKey = step.config.output_key || 'b2_result';
  const bucket = step.config.bucket;

  if (!objectKey) {
    return { success: false, error: 'Missing object_key in fetch_b2_result config' };
  }

  console.log(`[PIPELINE:fetch_b2_result] Fetching ${objectKey} from B2`);

  try {
    const url = generatePresignedUrl(objectKey, bucket);
    const response = await fetch(url);

    if (!response.ok) {
      return {
        success: false,
        error: `B2 download failed: ${response.status} ${response.statusText}`,
      };
    }

    const text = await response.text();
    let parsed: any;

    try {
      parsed = JSON.parse(text);
    } catch {
      // Not JSON — store as raw text
      parsed = text;
    }

    console.log(`[PIPELINE:fetch_b2_result] Downloaded ${text.length} bytes, parsed as ${typeof parsed}`);

    return {
      success: true,
      output: {
        [outputKey]: parsed,
        [`${outputKey}_raw_length`]: text.length,
        b2_object_key: objectKey,
      },
    };
  } catch (err: any) {
    return {
      success: false,
      error: `B2 fetch error: ${err.message}`,
    };
  }
}

registerStepExecutor('fetch_b2_result', executeFetchB2Result);
