import { describe, it, expect, beforeEach, afterEach } from 'vitest';
import { mkdtempSync, rmSync, readFileSync, readdirSync } from 'fs';
import { join, resolve } from 'path';
import { tmpdir } from 'os';
import { extractFinMessages } from '../src/adapters/swiftFinTcpAdapter';
import { forwardToOmnl } from '../src/outbound/omnlForwardAdapter';
import { buildGatewayMessage, submitToIntakeGateway } from '../src/outbound/intakeGatewayAdapter';
import { loadListenerConfig, buildResourceRegistry } from '../src/resourceRegistry';
import { mapToCanonical } from '../src/canonical/mapToCanonical';
import { parseSwiftFin } from '../src/parsers/inboundParser';
import type { SwiftListenerEvent } from '../src/types';

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

describe('swiftFinTcpAdapter', () => {
  it('extracts brace-delimited FIN messages from stream buffer', () => {
    const chunk = `{1:F01TEST}{2:I103TEST}{4::20:REF-1-}`;
    const { messages, remainder } = extractFinMessages(chunk);
    expect(messages.length).toBe(1);
    expect(messages[0]).toContain(':20:REF-1');
    expect(remainder).toBe('');
  });
});

describe('outbound adapters', () => {
  let tmp: string;

  beforeEach(() => {
    tmp = mkdtempSync(join(tmpdir(), 'swift-listener-'));
    process.env.OMNL_ISO20022_STORE_DIR = join(tmp, 'omnl');
    delete process.env.PRIVATE_KEY;
    process.env.SWIFT_LISTENER_GATEWAY_EXECUTE = '0';
  });

  afterEach(() => {
    delete process.env.OMNL_ISO20022_STORE_DIR;
    delete process.env.SWIFT_LISTENER_GATEWAY_EXECUTE;
    rmSync(tmp, { recursive: true, force: true });
  });

  it('forwards matched event to local OMNL store', async () => {
    const config = loadListenerConfig(ROOT);
    config.outbound = {
      omnlForward: {
        enabled: true,
        mode: 'local',
        tokenAggregationUrlEnv: 'TOKEN_AGGREGATION_URL',
        apiKeyEnv: 'OMNL_API_KEY',
        localStoreDir: 'config/iso20022-omnl/messages',
      },
      intakeGateway: config.outbound?.intakeGateway ?? {
        enabled: false,
        gatewayAddressEnv: 'ISO20022_INTAKE_GATEWAY_MAINNET',
        gatewayAddressFallback: '0x66C89F91A4e830d87fcE4E5F86aFA71A96FbDa3d',
        rpcUrlEnv: 'RPC_URL_138',
        privateKeyEnv: 'PRIVATE_KEY',
        executeEnv: 'SWIFT_LISTENER_GATEWAY_EXECUTE',
        dryRunDefault: true,
        chainId: 138,
      },
    };

    const parsed = parseSwiftFin(`{1:F01BANK}{2:I103BANK}{4::20:CHAIN138-x:70:cWUSDC-}`);
    const canonical = mapToCanonical(parsed, 'abc123');
    const event: SwiftListenerEvent = {
      id: 'evt-1',
      receivedAt: new Date().toISOString(),
      source: 'test',
      format: 'swift_fin',
      messageType: 'MT103',
      matched: true,
      matches: [],
      parsed: parsed.parsed,
      canonical,
      rawPreview: 'preview',
      dedupeKey: 'abc123',
    };

    const result = await forwardToOmnl(ROOT, config, event);
    expect(result.stored).toBeTruthy();
    const files = readdirSync(process.env.OMNL_ISO20022_STORE_DIR!);
    expect(files.length).toBe(1);
    const stored = JSON.parse(readFileSync(join(process.env.OMNL_ISO20022_STORE_DIR!, files[0]!), 'utf8'));
    expect(stored.instructionId).toContain('CHAIN138');
  });

  it('builds intake gateway message and dry-runs by default', async () => {
    const config = loadListenerConfig(ROOT);
    const registry = buildResourceRegistry(ROOT, config);
    config.outbound = {
      omnlForward: config.outbound?.omnlForward ?? {
        enabled: false,
        mode: 'local',
        tokenAggregationUrlEnv: 'TOKEN_AGGREGATION_URL',
        apiKeyEnv: 'OMNL_API_KEY',
        localStoreDir: 'config/iso20022-omnl/messages',
      },
      intakeGateway: {
        enabled: true,
        gatewayAddressEnv: 'ISO20022_INTAKE_GATEWAY_MAINNET',
        gatewayAddressFallback: '0x66C89F91A4e830d87fcE4E5F86aFA71A96FbDa3d',
        rpcUrlEnv: 'RPC_URL_138',
        privateKeyEnv: 'PRIVATE_KEY',
        executeEnv: 'SWIFT_LISTENER_GATEWAY_EXECUTE',
        dryRunDefault: true,
        chainId: 138,
      },
    };

    const parsed = parseSwiftFin(`{1:F01BANK}{2:I103BANK}{4::20:CHAIN138-x:32A:230101USD100,00-}`);
    const canonical = mapToCanonical(parsed, 'dedupe')!;
    const msg = buildGatewayMessage(canonical, registry);
    expect(msg.instructionId).toMatch(/^0x/);
    expect(msg.currencyCode).toMatch(/^0x/);

    const submit = await submitToIntakeGateway(config, registry, canonical);
    expect(submit.dryRun).toBe(true);
    expect(submit.prepared).toBe(true);
    expect(submit.gateway).toBeTruthy();
  });
});
