import { describe, it, expect, beforeEach, afterEach, vi } from 'vitest';
import { resolve } from 'path';
import { loadListenerConfig } from '../src/resourceRegistry';
import { createSwiftListener } from '../src/listener';
import { dispatchMatchedOutbounds } from '../src/outbound/dispatchOutcomes';
import { buildResourceRegistry } from '../src/resourceRegistry';
import { forwardToOmnl, OMNL_ISO20022_API_PATH } from '../src/outbound/omnlForwardAdapter';
import { mapToCanonical } from '../src/canonical/mapToCanonical';
import { parseSwiftFin } from '../src/parsers/inboundParser';
import type { SwiftListenerEvent } from '../src/types';

const ROOT = resolve(__dirname, '../../../..');

const SAMPLE_MT103 = `{1:F01BANKUS33AXXX0000000000}{2:I103BANKDEFFXXXXN}{4:
:20:CHAIN138-wire-test
:32A:230101USD500,00
:70:cWUSDC settlement chain138 0xf22258f57794CC8E06237084b353Ab30fFfa640b
-}`;

describe('core wiring defaults', () => {
  beforeEach(() => {
    delete process.env.PRIVATE_KEY;
    process.env.SWIFT_LISTENER_GATEWAY_EXECUTE = '0';
  });
  it('enables FIN TCP, OMNL forward, and intake gateway in config', () => {
    const config = loadListenerConfig(ROOT);
    expect(config.adapters.swiftFinTcp.enabled).toBe(true);
    expect(config.outbound?.omnlForward.enabled).toBe(true);
    expect(config.outbound?.intakeGateway.enabled).toBe(true);
    expect(config.outbound?.omnlForward.apiPath).toBe(OMNL_ISO20022_API_PATH);
  });
});

describe('OMNL POST /api/v1/omnl/iso20022/messages', () => {
  const originalFetch = global.fetch;

  beforeEach(() => {
    delete process.env.PRIVATE_KEY;
    process.env.SWIFT_LISTENER_GATEWAY_EXECUTE = '0';
    vi.stubGlobal(
      'fetch',
      vi.fn(async () => new Response(JSON.stringify({ id: 'omnl-1' }), { status: 201 }))
    );
  });

  afterEach(() => {
    global.fetch = originalFetch;
    vi.unstubAllGlobals();
  });

  it('POSTs matched event to token-aggregation OMNL ISO store', async () => {
    const config = loadListenerConfig(ROOT);
    const parsed = parseSwiftFin(SAMPLE_MT103);
    const canonical = mapToCanonical(parsed, 'wire-test-key')!;
    const event: SwiftListenerEvent = {
      id: 'evt-wire',
      receivedAt: new Date().toISOString(),
      source: 'test',
      format: 'swift_fin',
      messageType: 'MT103',
      matched: true,
      matches: [{ kind: 'cw_symbol', value: 'cWUSDC', field: 'test' }],
      parsed: parsed.parsed,
      canonical,
      rawPreview: SAMPLE_MT103,
      dedupeKey: 'wire-test-key',
    };

    const result = await forwardToOmnl(ROOT, config, event);
    expect(result.apiPath).toBe('/api/v1/omnl/iso20022/messages');
    expect(result.apiStatus).toBe(201);

    const fetchMock = global.fetch as ReturnType<typeof vi.fn>;
    expect(fetchMock).toHaveBeenCalledOnce();
    const [url, init] = fetchMock.mock.calls[0] as [string, RequestInit];
    expect(url).toContain('/api/v1/omnl/iso20022/messages');
    expect(init.method).toBe('POST');
    const body = JSON.parse(String(init.body));
    expect(body.messageType).toBe('pacs.008');
    expect(body.instructionId).toContain('CHAIN138');
  });
});

describe('ISO20022IntakeGateway on match', () => {
  beforeEach(() => {
    delete process.env.PRIVATE_KEY;
    process.env.SWIFT_LISTENER_GATEWAY_EXECUTE = '0';
    vi.stubGlobal(
      'fetch',
      vi.fn(async () => new Response(JSON.stringify({ id: 'omnl-gw' }), { status: 201 }))
    );
  });

  afterEach(() => {
    delete process.env.PRIVATE_KEY;
    delete process.env.SWIFT_LISTENER_GATEWAY_EXECUTE;
    vi.unstubAllGlobals();
  });

  it('dispatches intake gateway (prepared dry-run without key)', async () => {
    const config = loadListenerConfig(ROOT);
    const registry = buildResourceRegistry(ROOT, config);
    const parsed = parseSwiftFin(SAMPLE_MT103);
    const canonical = mapToCanonical(parsed, 'gateway-key')!;
    const event: SwiftListenerEvent = {
      id: 'evt-gw',
      receivedAt: new Date().toISOString(),
      source: 'test',
      format: 'swift_fin',
      messageType: 'MT103',
      matched: true,
      matches: [],
      parsed: parsed.parsed,
      canonical,
      rawPreview: SAMPLE_MT103,
      dedupeKey: 'gateway-key',
    };

    const outbound = await dispatchMatchedOutbounds(ROOT, config, registry, event);
    expect(outbound?.intakeGateway).toBeDefined();
    expect(outbound?.intakeGateway?.gateway).toBe('0x66C89F91A4e830d87fcE4E5F86aFA71A96FbDa3d');
    expect(outbound?.intakeGateway?.dryRun).toBe(true);
    expect(outbound?.intakeGateway?.prepared).toBe(true);
  });
});

describe('SWIFT FIN TCP feed', () => {
  it('is enabled and extracts FIN frames for TCP ingestion', async () => {
    const config = loadListenerConfig(ROOT);
    expect(config.adapters.swiftFinTcp.enabled).toBe(true);

    const { extractFinMessages } = await import('../src/adapters/swiftFinTcpAdapter');
    const { messages } = extractFinMessages(SAMPLE_MT103);
    expect(messages.length).toBe(1);
    expect(messages[0]).toContain('CHAIN138-wire-test');
  });
});

describe('processMessage end-to-end wiring', () => {
  const originalFetch = global.fetch;

  beforeEach(() => {
    delete process.env.PRIVATE_KEY;
    process.env.SWIFT_LISTENER_GATEWAY_EXECUTE = '0';
    vi.stubGlobal(
      'fetch',
      vi.fn(async () => new Response(JSON.stringify({ id: 'omnl-2' }), { status: 201 }))
    );
  });

  afterEach(() => {
    global.fetch = originalFetch;
    vi.unstubAllGlobals();
  });

  it('runs OMNL forward + intake gateway on matched message', async () => {
    const listener = createSwiftListener(ROOT);
    const event = await listener.processMessage({
      source: `wiring-e2e:${Date.now()}`,
      format: 'swift_fin',
      payload: SAMPLE_MT103,
    });

    expect(event?.matched).toBe(true);
    expect(event?.outbound?.omnl?.apiPath).toBe('/api/v1/omnl/iso20022/messages');
    expect(event?.outbound?.omnl?.apiStatus).toBe(201);
    expect(event?.outbound?.intakeGateway?.gateway).toBeTruthy();
  });
});
