import { readdirSync, readFileSync, statSync, renameSync, mkdirSync } from 'fs';
import { join, dirname } from 'path';
import type { InboundMessage, InboundFormat } from '../types';
import { detectInboundFormat } from '../parsers/inboundParser';

const INBOX_EXTENSIONS = /\.(fin|swift|xml|txt|json|as4|mx|telex|ktt)$/i;

export function scanDirectoryInbox(
  inboxDir: string,
  sourcePrefix: string,
  defaultFormat?: InboundFormat
): InboundMessage[] {
  const messages: InboundMessage[] = [];
  let entries: string[] = [];
  try {
    entries = readdirSync(inboxDir);
  } catch {
    return messages;
  }

  for (const name of entries.sort()) {
    const full = join(inboxDir, name);
    if (!statSync(full).isFile()) continue;
    if (name.startsWith('.')) continue;
    if (!INBOX_EXTENSIONS.test(name)) continue;
    const payload = readFileSync(full, 'utf8');
    const lower = name.toLowerCase();
    let format: InboundFormat = defaultFormat ?? detectInboundFormat(payload);
    if (lower.endsWith('.xml') || lower.endsWith('.mx') || lower.endsWith('.as4')) format = 'iso20022';
    if (lower.endsWith('.json')) format = 'json_broadcast';
    if (lower.endsWith('.telex') || lower.endsWith('.ktt')) format = 'ktt';
    messages.push({
      source: `${sourcePrefix}:${full}`,
      format,
      payload,
      metadata: { fileName: name, filePath: full },
    });
  }
  return messages;
}

export function archiveInboxFile(
  filePath: string,
  archiveDir: string
): void {
  mkdirSync(archiveDir, { recursive: true });
  const base = filePath.split('/').pop() ?? 'message';
  renameSync(filePath, join(archiveDir, `${Date.now()}-${base}`));
}

export function scanFileInbox(inboxDir: string): InboundMessage[] {
  return scanDirectoryInbox(inboxDir, 'file');
}

export async function pollP2pRailTransactions(options: {
  baseUrl: string;
  bearerToken?: string;
  apiKey?: string;
  path?: string;
  sinceId?: string;
}): Promise<InboundMessage[]> {
  const url = `${options.baseUrl.replace(/\/$/, '')}${options.path ?? '/api/transactions'}`;
  const headers: Record<string, string> = { Accept: 'application/json' };
  if (options.bearerToken) headers.Authorization = `Bearer ${options.bearerToken}`;
  if (options.apiKey) headers['X-API-Key'] = options.apiKey;

  const res = await fetch(url, { headers });
  if (!res.ok) {
    throw new Error(`P2P rail poll failed: ${res.status} ${res.statusText}`);
  }

  const body = (await res.json()) as unknown;
  const rows = Array.isArray(body) ? body : (body as { data?: unknown[] })?.data ?? [];
  const out: InboundMessage[] = [];

  for (const row of rows) {
    const obj = row as Record<string, unknown>;
    const id = String(obj.id ?? obj.transactionId ?? obj.reference ?? '');
    if (options.sinceId && id && id <= options.sinceId) continue;
    const payload =
      typeof obj.payload === 'string'
        ? obj.payload
        : typeof obj.swiftMessage === 'string'
          ? obj.swiftMessage
          : typeof obj.message === 'string'
            ? obj.message
            : JSON.stringify(obj);
    out.push({
      source: `p2p:${id || 'unknown'}`,
      format: detectInboundFormat(payload),
      payload,
      metadata: obj,
    });
  }
  return out;
}

export async function pollFinGatewayMessages(options: {
  baseUrl: string;
  bearerToken?: string;
  path?: string;
  sinceId?: string;
}): Promise<InboundMessage[]> {
  const url = `${options.baseUrl.replace(/\/$/, '')}${options.path ?? '/api/messages'}`;
  const headers: Record<string, string> = { Accept: 'application/json' };
  if (options.bearerToken) headers.Authorization = `Bearer ${options.bearerToken}`;

  const res = await fetch(url, { headers });
  if (!res.ok) {
    throw new Error(`FIN gateway poll failed: ${res.status} ${res.statusText}`);
  }

  const body = (await res.json()) as unknown;
  const rows = Array.isArray(body)
    ? body
    : (body as { messages?: unknown[]; data?: unknown[] })?.messages ??
      (body as { data?: unknown[] })?.data ??
      [];

  const out: InboundMessage[] = [];
  for (const row of rows) {
    const obj = row as Record<string, unknown>;
    const id = String(obj.id ?? obj.messageId ?? obj.reference ?? '');
    if (options.sinceId && id && id <= options.sinceId) continue;
    const payload =
      typeof obj.finMessage === 'string'
        ? obj.finMessage
        : typeof obj.swiftMessage === 'string'
          ? obj.swiftMessage
          : typeof obj.payload === 'string'
            ? obj.payload
            : typeof obj.body === 'string'
              ? obj.body
              : JSON.stringify(obj);
    out.push({
      source: `fin:${id || 'unknown'}`,
      format: detectInboundFormat(payload),
      payload,
      metadata: obj,
    });
  }
  return out;
}
