import { createHash, randomUUID } from 'crypto';
import { mkdirSync, writeFileSync } from 'fs';
import { join, resolve } from 'path';
import { canonicalToPacs008Xml } from '@dbis/checkpoint-core';
import type { ListenerConfig, SwiftListenerEvent } from '../types';

export const OMNL_ISO20022_API_PATH = '/api/v1/omnl/iso20022/messages';

type OmnlMessageType = 'pain.001' | 'pacs.008' | 'camt.053' | 'other';

function mapOmnlMessageType(event: SwiftListenerEvent): OmnlMessageType {
  const t = event.messageType.toLowerCase();
  if (t.includes('pain.001')) return 'pain.001';
  if (t.includes('pacs.008') || t === 'mt103') return 'pacs.008';
  if (t.includes('camt.053')) return 'camt.053';
  if (event.format === 'swift_fin' && event.messageType === 'MT103') return 'pacs.008';
  return 'other';
}

function payloadForStore(event: SwiftListenerEvent): string {
  if (event.format === 'iso20022') return event.rawPreview;
  if (event.canonical) return canonicalToPacs008Xml(event.canonical);
  return event.rawPreview;
}

function localStoreDir(projectRoot: string, config: ListenerConfig): string {
  const outbound = config.outbound?.omnlForward;
  const rel = outbound?.localStoreDir ?? 'config/iso20022-omnl/messages';
  const envDir = process.env.OMNL_ISO20022_STORE_DIR?.trim();
  if (envDir) return resolve(envDir);
  return resolve(projectRoot, rel);
}

export async function forwardToOmnl(
  projectRoot: string,
  config: ListenerConfig,
  event: SwiftListenerEvent
): Promise<{ stored?: string; apiStatus?: number; apiPath?: string; error?: string }> {
  const outbound = config.outbound?.omnlForward;
  const legacyEnabled = config.omnlForward?.enabled;
  if (!outbound?.enabled && !legacyEnabled) return {};

  const mode = outbound?.mode ?? 'local_and_api';
  const payload = payloadForStore(event);
  const messageType = mapOmnlMessageType(event);
  const result: { stored?: string; apiStatus?: number; apiPath?: string; error?: string } = {};

  if (mode === 'local' || mode === 'local_and_api') {
    const dir = localStoreDir(projectRoot, config);
    mkdirSync(dir, { recursive: true });
    const id = randomUUID();
    const receivedAt = new Date().toISOString();
    const payloadSha256 = createHash('sha256').update(payload, 'utf8').digest('hex');
    const record = {
      id,
      messageType,
      receivedAt,
      retentionUntil: new Date(
        new Date(receivedAt).setFullYear(new Date(receivedAt).getFullYear() + 10)
      ).toISOString(),
      uetr: event.canonical?.uetr,
      instructionId: event.canonical?.instructionId,
      settlementOrChainRef: event.canonical?.chain138TxHash || undefined,
      payloadSha256,
      payload,
      swiftListenerEventId: event.id,
      source: event.source,
    };
    writeFileSync(join(dir, `${id}.json`), JSON.stringify(record, null, 2), 'utf8');
    result.stored = id;
  }

  if (mode === 'api' || mode === 'local_and_api') {
    const urlEnv = outbound?.tokenAggregationUrlEnv ?? config.omnlForward?.tokenAggregationUrlEnv ?? 'TOKEN_AGGREGATION_URL';
    const keyEnv = outbound?.apiKeyEnv ?? config.omnlForward?.apiKeyEnv ?? 'OMNL_API_KEY';
    const base = process.env[urlEnv] ?? process.env.TOKEN_AGGREGATION_URL ?? 'http://127.0.0.1:3000';
    const apiKey = process.env[keyEnv] ?? process.env.OMNL_API_KEY;

    try {
      const headers: Record<string, string> = {
        'Content-Type': 'application/json',
        Accept: 'application/json',
      };
      if (apiKey) headers['X-API-Key'] = apiKey;

      const apiPath = outbound?.apiPath ?? OMNL_ISO20022_API_PATH;
      const res = await fetch(`${base.replace(/\/$/, '')}${apiPath}`, {
        method: 'POST',
        headers,
        body: JSON.stringify({
          messageType,
          payload,
          uetr: event.canonical?.uetr,
          instructionId: event.canonical?.instructionId,
          settlementOrChainRef: event.canonical?.chain138TxHash,
          accountingRef: `swift-listener:${event.id}`,
        }),
      });
      result.apiStatus = res.status;
      result.apiPath = apiPath;
      if (!res.ok) result.error = `OMNL API ${res.status}`;
    } catch (e) {
      result.error = e instanceof Error ? e.message : String(e);
    }
  }

  return result;
}
