import { readFileSync, existsSync } from 'fs';
import { resolve } from 'path';
import { createHash, randomUUID } from 'crypto';
import axios from 'axios';
import { fetchFineractGlAccounts } from './omnl-ipsas-gl';
import {
  fineractHeaders,
  fetchOfficeJournalLines,
  signedGlBalances,
} from './omnl-fineract-office-gl';
import { alchemyConfigured } from './omnl-alchemy-client';
import {
  loadTerminalConnection,
  buildConnectionPublicSummary,
  connectionAlchemyApiKey,
  connectionReceiverConfig,
  type TerminalConnectionProfile,
} from './omnl-terminal-connection';

export type TerminalScreenMode = 'black' | 'color';

export type TerminalConfig = {
  version: string;
  defaultSettlementRef: string;
  defaultOfficeId: number;
  defaultCurrency: string;
  defaultValueDate: string;
  defaultBeneficiary: string;
  defaultBeneficiaryOfficeId: number;
  defaultBeneficiaryExternalId: string;
  defaultConnectionId?: string;
  omnlLegalName: string;
  omnlLei: string;
  pofPrimaryGlCode: string;
  debitNoteDefaultDebitGl: string;
  debitNoteDefaultCreditGl: string;
  tokenization: {
    chain138RpcEnv: string;
    defaultLineId: string;
    sanitizedRedactFields: string[];
  };
  alchemy: {
    mainnetNetwork: string;
    chain138Network: string;
    allowedRpcMethods: string[];
    pricesApiPath: string;
  };
  disclaimers: Record<string, string>;
};

export type SettlementTerminalContext = {
  generatedAtUtc: string;
  settlementRef: string;
  valueDate: string;
  currency: string;
  officeId: number;
  officeName: string;
  beneficiary: string;
  beneficiaryOfficeId: number;
  beneficiaryExternalId: string;
  amountUsd: string;
  omnlLegalName: string;
  omnlLei: string;
  fineract: {
    configured: boolean;
    baseUrl?: string;
    glCount?: number;
    pofPrimaryGlCode: string;
    pofPrimaryAmountUsd: string | null;
    glBalances?: Array<{ glCode: string; name: string; side: string; amount: string }>;
    error?: string;
  };
  alchemy: {
    configured: boolean;
    mainnetNetwork: string;
    chain138Network: string;
  };
  documentSha256: string;
  connection?: {
    connectionId: string;
    summary: Record<string, unknown>;
    receiverConfigured: boolean;
    alchemyConfigured: boolean;
  };
};

export type TerminalArtifact =
  | 'package'
  | 'proof-of-funds'
  | 'debit-note'
  | 'remittance-advice'
  | 'tokenization-sanitized'
  | 'conversion-swift-copy';

function repoRoot(): string {
  if (process.env.PROXMOX_ROOT?.trim()) return process.env.PROXMOX_ROOT.trim();
  if (process.env.PHOENIX_REPO_ROOT?.trim()) return process.env.PHOENIX_REPO_ROOT.trim();
  // dist/services → proxmox repo root (5 levels)
  return resolve(__dirname, '../../../../..');
}

export function loadTerminalConfig(): TerminalConfig {
  const candidates = [
    process.env.OMNL_SETTLEMENT_TERMINAL_CONFIG?.trim(),
    resolve(repoRoot(), 'config/omnl-settlement-terminal.v1.json'),
    resolve(__dirname, '../../../../config/omnl-settlement-terminal.v1.json'),
  ].filter(Boolean) as string[];
  const p = candidates.find((c) => existsSync(c));
  if (!p) {
    throw new Error(`Terminal config not found (tried: ${candidates.join(', ')})`);
  }
  return JSON.parse(readFileSync(p, 'utf8')) as TerminalConfig;
}

async function fetchOffices(): Promise<Array<{ id: number; name: string }>> {
  const { base, headers } = fineractHeaders();
  const { data } = await axios.get(`${base}/offices`, { headers, timeout: 60000 });
  const rows = Array.isArray(data) ? data : [];
  return rows.map((r: { id?: number; name?: string }) => ({
    id: Number(r.id),
    name: String(r.name || ''),
  }));
}

function glCreditAmount(rows: Array<{ glCode: string; side: string; amount: string }>, code: string): string | null {
  for (const r of rows) {
    if (r.glCode === code) {
      if (r.side === 'Cr') return r.amount;
      if (r.side === 'Dr') return `-${r.amount}`;
    }
  }
  return null;
}

function maskValue(field: string, value: string): string {
  if (!value) return value;
  if (field.toLowerCase().includes('swift') || field.toLowerCase().includes('iban')) {
    return value.length <= 4 ? '****' : `${'*'.repeat(Math.max(0, value.length - 4))}${value.slice(-4)}`;
  }
  if (field.toLowerCase().includes('name')) {
    const parts = value.split(/\s+/);
    return parts.map((p) => (p.length <= 2 ? p : `${p[0]}${'*'.repeat(p.length - 2)}${p.slice(-1)}`)).join(' ');
  }
  if (field.toLowerCase().includes('account')) {
    return value.length <= 4 ? '****' : `****${value.slice(-4)}`;
  }
  return '***REDACTED***';
}

export async function buildSettlementTerminalContext(input: {
  settlementRef?: string;
  officeId?: number;
  valueDate?: string;
  amountUsd?: string;
  connectionId?: string;
}): Promise<SettlementTerminalContext> {
  const cfg = loadTerminalConfig();
  let connectionProfile: TerminalConnectionProfile | null = null;
  const connId = input.connectionId?.trim() || cfg.defaultConnectionId?.trim() || '';
  if (connId) {
    try {
      connectionProfile = loadTerminalConnection(connId);
    } catch {
      connectionProfile = null;
    }
  }
  const sd = connectionProfile?.settlementDefaults;
  const settlementRef =
    input.settlementRef?.trim() || sd?.settlementRef || cfg.defaultSettlementRef;
  const officeId = input.officeId ?? cfg.defaultOfficeId;
  const valueDate = input.valueDate?.trim() || cfg.defaultValueDate;
  const amountUsd = input.amountUsd?.trim() || '1000000000.00';

  let officeName = `Office ${officeId}`;
  let glBalances: Array<{ glCode: string; name: string; side: string; amount: string }> = [];
  let pofAmount: string | null = null;
  let fineractError: string | undefined;
  let glCount = 0;
  let baseUrl: string | undefined;

  try {
    const { base } = fineractHeaders();
    baseUrl = base;
    const [offices, glRows, lines] = await Promise.all([
      fetchOffices(),
      fetchFineractGlAccounts(),
      fetchOfficeJournalLines(officeId),
    ]);
    glCount = glRows.length;
    const hit = offices.find((o) => o.id === officeId);
    if (hit) officeName = hit.name;
    const glById = new Map<number, { glCode: string; name: string }>();
    for (const g of glRows) {
      if (g.id != null && g.glCode) {
        glById.set(g.id, { glCode: String(g.glCode), name: String(g.name || '') });
      }
    }
    glBalances = signedGlBalances(lines, glById);
    pofAmount = glCreditAmount(glBalances, cfg.pofPrimaryGlCode);
  } catch (e) {
    fineractError = e instanceof Error ? e.message : String(e);
  }

  const ctx: SettlementTerminalContext = {
    generatedAtUtc: new Date().toISOString(),
    settlementRef,
    valueDate,
    currency: cfg.defaultCurrency,
    officeId,
    officeName,
    beneficiary: sd?.beneficiary || cfg.defaultBeneficiary,
    beneficiaryOfficeId: cfg.defaultBeneficiaryOfficeId,
    beneficiaryExternalId: sd?.beneficiaryExternalId || cfg.defaultBeneficiaryExternalId,
    amountUsd,
    omnlLegalName: cfg.omnlLegalName,
    omnlLei: cfg.omnlLei,
    fineract: {
      configured: !fineractError,
      baseUrl,
      glCount,
      pofPrimaryGlCode: cfg.pofPrimaryGlCode,
      pofPrimaryAmountUsd: pofAmount,
      glBalances: glBalances.length ? glBalances : undefined,
      error: fineractError,
    },
    alchemy: {
      configured: connectionProfile
        ? Boolean(connectionAlchemyApiKey(connectionProfile))
        : alchemyConfigured(),
      mainnetNetwork: cfg.alchemy.mainnetNetwork,
      chain138Network: cfg.alchemy.chain138Network,
    },
    documentSha256: '',
  };
  if (connectionProfile) {
    const receiver = connectionReceiverConfig(connectionProfile);
    ctx.connection = {
      connectionId: connectionProfile.connectionId,
      summary: buildConnectionPublicSummary(connectionProfile),
      receiverConfigured: receiver.configured,
      alchemyConfigured: Boolean(connectionAlchemyApiKey(connectionProfile)),
    };
    ctx.currency = sd?.currency || ctx.currency;
  }
  ctx.documentSha256 = createHash('sha256').update(JSON.stringify(ctx)).digest('hex');
  return ctx;
}

export function buildProofOfFunds(ctx: SettlementTerminalContext): Record<string, unknown> {
  const cfg = loadTerminalConfig();
  return {
    documentType: 'proof-of-funds',
    generatedAtUtc: ctx.generatedAtUtc,
    settlementRef: ctx.settlementRef,
    valueDate: ctx.valueDate,
    issuer: {
      legalName: ctx.omnlLegalName,
      lei: ctx.omnlLei,
      officeId: ctx.officeId,
      officeName: ctx.officeName,
    },
    attestation: {
      primaryGlCode: ctx.fineract.pofPrimaryGlCode,
      primaryAmountUsd: ctx.fineract.pofPrimaryAmountUsd,
      currency: ctx.currency,
      methodology: 'Fineract journal sum (DEBIT +, CREDIT -) for office GL balances',
    },
    glBalances: ctx.fineract.glBalances ?? [],
    disclaimer: cfg.disclaimers.pof,
    integrity: { sha256: ctx.documentSha256 },
  };
}

export function buildDebitNote(ctx: SettlementTerminalContext): Record<string, unknown> {
  const cfg = loadTerminalConfig();
  const uetr = randomUUID();
  return {
    documentType: 'debit-note',
    generatedAtUtc: ctx.generatedAtUtc,
    settlementRef: ctx.settlementRef,
    valueDate: ctx.valueDate,
    uetr,
    debitNoteId: `DN-${ctx.settlementRef}-${ctx.valueDate.replace(/-/g, '')}`,
    orderingInstitution: {
      name: ctx.omnlLegalName,
      lei: ctx.omnlLei,
      officeId: ctx.officeId,
    },
    beneficiary: {
      name: ctx.beneficiary,
      officeId: ctx.beneficiaryOfficeId,
      externalId: ctx.beneficiaryExternalId,
      ...(ctx.connection?.summary?.client ? { client: ctx.connection.summary.client } : {}),
      ...(ctx.connection?.summary?.bank ? { bank: ctx.connection.summary.bank } : {}),
    },
    receiver: ctx.connection
      ? {
          connectionId: ctx.connection.connectionId,
          serverId: (ctx.connection.summary.receiver as { serverId?: string })?.serverId,
          receiverIp: (ctx.connection.summary.receiver as { receiverIp?: string })?.receiverIp,
          wallet: (ctx.connection.summary as { wallet?: { address?: string } }).wallet,
        }
      : undefined,
    accounting: {
      debitGlCode: cfg.debitNoteDefaultDebitGl,
      creditGlCode: cfg.debitNoteDefaultCreditGl,
      amountUsd: ctx.amountUsd,
      currency: ctx.currency,
      narrative: `Settlement debit — ${ctx.settlementRef}`,
    },
    fineractMapped: ctx.fineract.configured,
    disclaimer: 'Accounting debit note mapped to OMNL Fineract IPSAS journal pairs — not a bank wire debit advice.',
    integrity: { sha256: createHash('sha256').update(JSON.stringify(ctx)).digest('hex') },
  };
}

export function buildRemittanceAdvice(ctx: SettlementTerminalContext): Record<string, unknown> {
  const instructionId = `RA-${ctx.settlementRef}-${Date.now()}`;
  return {
    documentType: 'remittance-advice',
    generatedAtUtc: ctx.generatedAtUtc,
    settlementRef: ctx.settlementRef,
    valueDate: ctx.valueDate,
    instructionId,
    messageType: 'camt.054-equivalent',
    remitter: {
      name: ctx.omnlLegalName,
      lei: ctx.omnlLei,
      officeId: ctx.officeId,
      officeName: ctx.officeName,
    },
    beneficiary: {
      name: ctx.beneficiary,
      officeId: ctx.beneficiaryOfficeId,
      externalId: ctx.beneficiaryExternalId,
      ...(ctx.connection?.summary?.client ? { client: ctx.connection.summary.client } : {}),
      ...(ctx.connection?.summary?.bank ? { bank: ctx.connection.summary.bank } : {}),
    },
    receiver: ctx.connection
      ? {
          connectionId: ctx.connection.connectionId,
          configured: ctx.connection.receiverConfigured,
          serverId: (ctx.connection.summary.receiver as { serverId?: string })?.serverId,
          apiDocsUrl: (ctx.connection.summary.receiver as { apiDocsUrl?: string })?.apiDocsUrl,
        }
      : undefined,
    payment: {
      amount: ctx.amountUsd,
      currency: ctx.currency,
      valueDate: ctx.valueDate,
      remittanceInfo: `Settlement ${ctx.settlementRef} — OMNL/HYBX book transfer`,
    },
    fineractStatus: ctx.fineract.configured ? 'sor_reachable' : 'sor_unavailable',
    disclaimer: 'Remittance advice for institutional reconciliation — confirm against Fineract JE and ISO 20022 archive.',
    integrity: { sha256: createHash('sha256').update(instructionId + ctx.settlementRef).digest('hex') },
  };
}

export function buildTokenizationSanitized(ctx: SettlementTerminalContext): Record<string, unknown> {
  const cfg = loadTerminalConfig();
  const raw = {
    documentType: 'tokenization-sanitized',
    generatedAtUtc: ctx.generatedAtUtc,
    settlementRef: ctx.settlementRef,
    lineId: cfg.tokenization.defaultLineId,
    tokenizationRequestId: `TOK-${ctx.settlementRef}-${randomUUID().slice(0, 8)}`,
    underlyingAsset: ctx.currency,
    amount: ctx.amountUsd,
    issuer: ctx.omnlLegalName,
    beneficiaryName: ctx.beneficiary,
    orderingName: ctx.officeName,
    accountNumber: `00000000${ctx.officeId}`.slice(-12),
    iban: `US00OMNL000000000${ctx.officeId}`,
    swiftBic: 'OMNLUS33',
    chain138Line: cfg.tokenization.defaultLineId,
    policyNote: 'Sanitized copy for external distribution — full tokenization record retained operator-side.',
  };
  const sanitized: Record<string, unknown> = { ...raw };
  for (const field of cfg.tokenization.sanitizedRedactFields) {
    const val = raw[field as keyof typeof raw];
    if (typeof val === 'string') {
      sanitized[field] = maskValue(field, val);
    }
  }
  sanitized.integrity = {
    sha256: createHash('sha256').update(JSON.stringify(sanitized)).digest('hex'),
    fullRecordHint: 'POST /api/v1/omnl/iso20022/messages for retained ISO payload',
  };
  return sanitized;
}

export function buildConversionSwiftCopy(ctx: SettlementTerminalContext): Record<string, unknown> {
  const cfg = loadTerminalConfig();
  return {
    documentType: 'conversion-swift-copy',
    generatedAtUtc: ctx.generatedAtUtc,
    settlementRef: ctx.settlementRef,
    valueDate: ctx.valueDate,
    copyClass: 'internal-operator-conversion',
    fields: {
      ':20:': ctx.settlementRef,
      ':23B:': 'CRED',
      ':32A:': `${ctx.valueDate.replace(/-/g, '').slice(2)}${ctx.currency}${ctx.amountUsd.replace('.', ',')}`,
      ':50K:': `/${ctx.omnlLei}\n${ctx.omnlLegalName}\nOffice ${ctx.officeId}`,
      ':59:': `/${ctx.beneficiaryExternalId}\n${ctx.beneficiary}`,
      ':70:': `CONVERSION SETTLEMENT ${ctx.settlementRef}`,
      ':71A:': 'SHA',
    },
    iso20022Equivalent: 'pacs.008 / pacs.009 mapping available via /api/v1/omnl/iso20022/messages',
    fineractMapped: ctx.fineract.configured,
    disclaimer: cfg.disclaimers.swiftCopy,
    integrity: { sha256: createHash('sha256').update(ctx.settlementRef + ctx.valueDate).digest('hex') },
  };
}

export function buildFullTerminalPackage(ctx: SettlementTerminalContext): Record<string, unknown> {
  const cfg = loadTerminalConfig();
  const artifacts = {
    proofOfFunds: buildProofOfFunds(ctx),
    debitNote: buildDebitNote(ctx),
    remittanceAdvice: buildRemittanceAdvice(ctx),
    tokenizationSanitized: buildTokenizationSanitized(ctx),
    conversionSwiftCopy: buildConversionSwiftCopy(ctx),
    screens: {
      black: renderTerminalScreen(ctx, 'black', 'package'),
      color: renderTerminalScreen(ctx, 'color', 'package'),
    },
  };
  return {
    documentType: 'omnl-settlement-terminal-package',
    version: cfg.version,
    generatedAtUtc: ctx.generatedAtUtc,
    settlementRef: ctx.settlementRef,
    context: ctx,
    artifacts,
    disclaimers: cfg.disclaimers,
    integrity: {
      packageSha256: createHash('sha256').update(JSON.stringify(artifacts)).digest('hex'),
    },
  };
}

function artifactPayload(
  ctx: SettlementTerminalContext,
  artifact: TerminalArtifact
): Record<string, unknown> {
  switch (artifact) {
    case 'proof-of-funds':
      return buildProofOfFunds(ctx);
    case 'debit-note':
      return buildDebitNote(ctx);
    case 'remittance-advice':
      return buildRemittanceAdvice(ctx);
    case 'tokenization-sanitized':
      return buildTokenizationSanitized(ctx);
    case 'conversion-swift-copy':
      return buildConversionSwiftCopy(ctx);
    case 'package':
    default:
      return buildFullTerminalPackage(ctx);
  }
}

export function renderTerminalScreen(
  ctx: SettlementTerminalContext,
  mode: TerminalScreenMode,
  artifact: TerminalArtifact = 'package'
): string {
  const cfg = loadTerminalConfig();
  const lines: string[] = [];
  const hr = '='.repeat(72);
  lines.push(hr);
  lines.push(' OMNL / HYBX SETTLEMENT TERMINAL');
  lines.push(` MODE: ${mode === 'black' ? 'BLACK / GREEN' : 'COLOR / GRAY'}`);
  lines.push(` ARTIFACT: ${artifact.toUpperCase()}`);
  lines.push(hr);
  lines.push(` SETTLEMENT REF .... ${ctx.settlementRef}`);
  lines.push(` VALUE DATE ........ ${ctx.valueDate}`);
  lines.push(` OFFICE ............ ${ctx.officeId} — ${ctx.officeName}`);
  lines.push(` BENEFICIARY ....... ${ctx.beneficiary}`);
  lines.push(` AMOUNT USD ........ ${ctx.amountUsd}`);
  lines.push(` GENERATED UTC ..... ${ctx.generatedAtUtc}`);
  lines.push('-'.repeat(72));
  lines.push(' FINERACT SoR');
  lines.push(`   STATUS .......... ${ctx.fineract.configured ? 'REACHABLE' : 'UNAVAILABLE'}`);
  if (ctx.fineract.baseUrl) lines.push(`   BASE URL ........ ${ctx.fineract.baseUrl}`);
  if (ctx.fineract.glCount != null) lines.push(`   GL ACCOUNTS ..... ${ctx.fineract.glCount}`);
  lines.push(
    `   POF GL ${ctx.fineract.pofPrimaryGlCode} .... ${ctx.fineract.pofPrimaryAmountUsd ?? 'N/A'} ${ctx.currency}`
  );
  if (ctx.fineract.error) lines.push(`   ERROR ........... ${ctx.fineract.error}`);
  lines.push('-'.repeat(72));
  lines.push(' ALCHEMY');
  lines.push(`   CONFIGURED ...... ${ctx.alchemy.configured ? 'YES' : 'NO (set ALCHEMY_API_KEY)'}`);
  lines.push(`   MAINNET ......... ${ctx.alchemy.mainnetNetwork}`);
  lines.push(`   CHAIN 138 ....... ${ctx.alchemy.chain138Network}`);
  if (ctx.connection) {
    lines.push('-'.repeat(72));
    lines.push(' RECEIVER CONNECTION');
    lines.push(`   ID .............. ${ctx.connection.connectionId}`);
    const recv = ctx.connection.summary.receiver as { serverId?: string; receiverIp?: string };
    if (recv?.serverId) lines.push(`   SERVER .......... ${recv.serverId}`);
    if (recv?.receiverIp) lines.push(`   IP .............. ${recv.receiverIp}`);
    const wallet = (ctx.connection.summary as { wallet?: { address?: string } }).wallet?.address;
    if (wallet) lines.push(`   ETH WALLET ...... ${wallet}`);
    lines.push(`   RECEIVER API .... ${ctx.connection.receiverConfigured ? 'CONFIGURED' : 'KEYS UNSET'}`);
    lines.push(`   ALCHEMY CONN .... ${ctx.connection.alchemyConfigured ? 'CONFIGURED' : 'KEY UNSET'}`);
  }
  lines.push('-'.repeat(72));
  if (artifact !== 'package') {
    const doc = artifactPayload(ctx, artifact);
    lines.push(` DOCUMENT: ${String(doc.documentType || artifact)}`);
    if (doc.disclaimer) lines.push(` NOTE: ${String(doc.disclaimer)}`);
    if (typeof doc.integrity === 'object' && doc.integrity && 'sha256' in (doc.integrity as object)) {
      lines.push(` SHA256: ${String((doc.integrity as { sha256: string }).sha256).slice(0, 16)}...`);
    }
  } else {
    lines.push(' ARTIFACTS: POF | DEBIT-NOTE | REMITTANCE | TOKEN-SAN | SWIFT-COPY');
  }
  lines.push('-'.repeat(72));
  lines.push(` ${cfg.disclaimers.terminalScreen}`);
  lines.push(hr);
  lines.push(' END OF TERMINAL COPY');
  lines.push(hr);
  return lines.join('\n');
}

export function getTerminalArtifact(
  ctx: SettlementTerminalContext,
  artifact: TerminalArtifact
): Record<string, unknown> {
  return artifactPayload(ctx, artifact);
}
