import { Router, Request, Response } from 'express';
import { Contract, JsonRpcProvider, type InterfaceAbi } from 'ethers';
import { omnlRateLimiter } from '../middleware/rate-limit';
import { omnlRequireApiKeyInProduction } from '../middleware/omnl-guards';
import { omnlAuditMiddleware } from '../middleware/omnl-audit-middleware';
import {
  fetchOmnlCompliance,
  fetchOmnlComplianceAggregated,
  fetchLatestAttestation,
  fetchBreakerStatus,
  loadCrossChainLines,
  loadCrossChainConfigPath,
} from '../../services/omnl-compliance';
import { omnlComplianceCore138, omnlReserveStore138 } from '../../services/omnl-chain138-addresses';
import { computeOmnlReconcileAnchor } from '../../services/omnl-reconcile-anchor';
import { getOmnlIntegrationStatus } from '../../services/omnl-integration-status';
import { getOmnlApiCatalog } from '../../services/omnl-api-catalog';
import omnlOpenApi from '../../resources/omnl-openapi.json';

const router = Router();
router.use(omnlRateLimiter);
router.use(omnlAuditMiddleware);
router.use(omnlRequireApiKeyInProduction);

const REGISTRY_ABI: InterfaceAbi = [
  'function allLineIds() view returns (bytes32[])',
  'function getLine(bytes32 lineId) view returns (tuple(address tokenM0,address tokenM1,uint8 decimals,uint16 iso4217Numeric,bool isXAU,bool active))',
];

const COORDINATOR_ABI: InterfaceAbi = [
  'function mirrorChainSelector() view returns (uint64)',
  'function mirrorReceiver() view returns (address)',
  'function feeToken() view returns (address)',
];

/**
 * GET /omnl/openapi.json — OpenAPI 3.0 document for Swagger UI / codegen.
 */
router.get('/omnl/openapi.json', (_req: Request, res: Response) => {
  res.type('application/json').json(omnlOpenApi as Record<string, unknown>);
});

/**
 * GET /omnl/catalog — machine-readable list of OMNL HTTP integrations.
 */
router.get('/omnl/catalog', (_req: Request, res: Response) => {
  res.json(getOmnlApiCatalog());
});

/**
 * GET /omnl/integration-status — which env-backed integrations are present (no secrets).
 */
router.get('/omnl/integration-status', (_req: Request, res: Response) => {
  res.json({
    generatedAt: new Date().toISOString(),
    ...getOmnlIntegrationStatus(),
  });
});

/**
 * GET /omnl/reconcile-anchor — same SHA-256 as omnl-reconcile-report.mjs (IPSAS + matrix files).
 */
router.get('/omnl/reconcile-anchor', (_req: Request, res: Response) => {
  const out = computeOmnlReconcileAnchor();
  if (!out.ok) {
    res.status(503).json(out);
    return;
  }
  res.json(out);
});

/**
 * GET /omnl/cross-chain-lines — logical lines from hybx-omnl-cross-chain-lines.json.
 */
router.get('/omnl/cross-chain-lines', (_req: Request, res: Response) => {
  try {
    res.json({
      configPath: loadCrossChainConfigPath(),
      lines: loadCrossChainLines(),
    });
  } catch (e) {
    const msg = e instanceof Error ? e.message : String(e);
    res.status(500).json({ error: msg });
  }
});

/**
 * GET /omnl/mirror-coordinator?chainId=138 — on-chain mirror destination (CCIP).
 */
/**
 * GET /omnl/zk-verifier — env-configured verifier address (placeholder ok).
 */
router.get('/omnl/zk-verifier', (_req: Request, res: Response) => {
  const a = process.env.OMNL_ZK_VERIFIER?.trim() || '';
  res.json({
    configured: Boolean(a),
    address: a || null,
    note: 'See docs/hybx-omnl/ZK_INTEGRATION.md for wiring a real verifier.',
  });
});

router.get('/omnl/mirror-coordinator', async (req: Request, res: Response) => {
  try {
    const chainId = parseInt(String(req.query.chainId || '138'), 10);
    const coord =
      process.env[`OMNL_MIRROR_COORDINATOR_${chainId}`]?.trim() ||
      process.env.OMNL_MIRROR_COORDINATOR?.trim();
    if (!coord) {
      res.status(503).json({
        error: 'OMNL_MIRROR_COORDINATOR or OMNL_MIRROR_COORDINATOR_<chainId> not set',
        chainId,
      });
      return;
    }
    const rpc = rpcUrl(chainId);
    if (!rpc) {
      res.status(503).json({ error: 'RPC not configured for chain', chainId });
      return;
    }
    const c = new Contract(coord, COORDINATOR_ABI, new JsonRpcProvider(rpc, chainId));
    const [mirrorChainSelector, mirrorReceiver, feeToken] = await Promise.all([
      c.mirrorChainSelector(),
      c.mirrorReceiver(),
      c.feeToken(),
    ]);
    res.json({
      chainId,
      coordinator: coord,
      mirrorChainSelector: String(mirrorChainSelector),
      mirrorReceiver,
      feeToken,
    });
  } catch (e) {
    const msg = e instanceof Error ? e.message : String(e);
    res.status(500).json({ error: msg });
  }
});

function addrForChain(chainId: number): string | undefined {
  if (chainId === 138) {
    return omnlComplianceCore138();
  }
  if (chainId === 651940) {
    return process.env.OMNL_COMPLIANCE_CORE_651940;
  }
  return process.env[`OMNL_COMPLIANCE_CORE_${chainId}`];
}

function rpcUrl(chainId: number): string | undefined {
  if (chainId === 138) return process.env.RPC_URL_138 || process.env.RPC_URL;
  if (chainId === 651940) return process.env.CHAIN_651940_RPC_URL || 'https://mainnet-rpc.alltra.global';
  return process.env[`CHAIN_${chainId}_RPC_URL`];
}

/**
 * GET /omnl/compliance/:lineId?chainId=138|651940
 */
router.get('/omnl/compliance/:lineId', async (req: Request, res: Response) => {
  try {
    const chainId = parseInt(String(req.query.chainId || '138'), 10);
    const lineId = req.params.lineId as string;
    const addr = addrForChain(chainId);
    if (!addr) {
      res.status(503).json({
        error: 'OMNL_COMPLIANCE_CORE not configured for this chain',
        chainId,
      });
      return;
    }
    const snap = await fetchOmnlCompliance(chainId, lineId, addr);
    res.json(snap);
  } catch (e) {
    const msg = e instanceof Error ? e.message : String(e);
    res.status(500).json({ error: msg });
  }
});

/**
 * GET /omnl/compliance-aggregated/:lineId — summed supply across chains (see hybx-omnl-cross-chain-lines.json).
 */
router.get('/omnl/compliance-aggregated/:lineId', async (req: Request, res: Response) => {
  try {
    const lineId = req.params.lineId as string;
    const snap = await fetchOmnlComplianceAggregated(lineId);
    res.json(snap);
  } catch (e) {
    const msg = e instanceof Error ? e.message : String(e);
    res.status(500).json({ error: msg });
  }
});

/**
 * GET /omnl/instruments?chainId=138 — InstrumentRegistry.allLineIds + getLine
 */
router.get('/omnl/instruments', async (req: Request, res: Response) => {
  try {
    const chainId = parseInt(String(req.query.chainId || '138'), 10);
    const reg = process.env[`OMNL_INSTRUMENT_REGISTRY_${chainId}`] || process.env.OMNL_INSTRUMENT_REGISTRY_138;
    const rpc = rpcUrl(chainId);
    if (!reg || !rpc) {
      res.status(503).json({ error: 'OMNL_INSTRUMENT_REGISTRY_* and RPC required', chainId });
      return;
    }
    const c = new Contract(reg, REGISTRY_ABI, new JsonRpcProvider(rpc, chainId));
    const ids = (await c.allLineIds()) as string[];
    const lines = await Promise.all(
      ids.map(async (id: string) => {
        const line = await c.getLine(id);
        return { lineId: id, line };
      })
    );
    res.json({ chainId, lines, crossChainConfigLines: loadCrossChainLines().length });
  } catch (e) {
    const msg = e instanceof Error ? e.message : String(e);
    res.status(500).json({ error: msg });
  }
});

/**
 * GET /omnl/attestations/:lineId?chainId=138 — ReserveCommitmentStore.getCommitment
 */
router.get('/omnl/attestations/:lineId', async (req: Request, res: Response) => {
  try {
    const chainId = parseInt(String(req.query.chainId || '138'), 10);
    const lineId = req.params.lineId as string;
    const out = await fetchLatestAttestation(chainId, lineId);
    res.json({ chainId, lineId, ...out });
  } catch (e) {
    const msg = e instanceof Error ? e.message : String(e);
    res.status(500).json({ error: msg });
  }
});

/**
 * GET /omnl/breaker?chainId=138&lineId=0x...
 */
router.get('/omnl/breaker', async (req: Request, res: Response) => {
  try {
    const chainId = parseInt(String(req.query.chainId || '138'), 10);
    const lineId = String(req.query.lineId || '');
    if (!lineId || lineId === 'undefined') {
      res.status(400).json({ error: 'lineId query parameter required' });
      return;
    }
    const s = await fetchBreakerStatus(chainId, lineId);
    res.json({ chainId, lineId, ...s });
  } catch (e) {
    const msg = e instanceof Error ? e.message : String(e);
    res.status(500).json({ error: msg });
  }
});

/**
 * GET /omnl/mirror-status/:lineId — compare reserve version (and R) across 138 and 651940.
 */
router.get('/omnl/mirror-status/:lineId', async (req: Request, res: Response) => {
  try {
    const lineId = req.params.lineId as string;
    const a138 = omnlReserveStore138();
    const a651940 = process.env.OMNL_RESERVE_STORE_651940;
    const out: Record<string, unknown> = {
      lineId,
      configured138: Boolean(a138),
      configured651940: Boolean(a651940),
    };
    if (!a138 || !a651940) {
      res.status(503).json({
        ...out,
        error: 'Set OMNL_RESERVE_STORE_138 and OMNL_RESERVE_STORE_651940 for mirror comparison',
      });
      return;
    }
    const [x138, x651] = await Promise.all([
      fetchLatestAttestation(138, lineId),
      fetchLatestAttestation(651940, lineId),
    ]);
    out.attestation138 = x138;
    out.attestation651940 = x651;
    out.rSynced = x138.r === x651.r;
    out.versionSynced = x138.version === x651.version;
    out.merkleSynced = x138.merkleRoot === x651.merkleRoot;
    out.inSync = out.rSynced && out.versionSynced;
    res.json(out);
  } catch (e) {
    const msg = e instanceof Error ? e.message : String(e);
    res.status(500).json({ error: msg });
  }
});

/**
 * GET /omnl/health — requires OMNL_HEALTH_LINE_ID when comparing chains (no default zero line).
 */
router.get('/omnl/health', async (req: Request, res: Response) => {
  const line = process.env.OMNL_HEALTH_LINE_ID;
  if (!line) {
    res.status(400).json({
      error: 'Set OMNL_HEALTH_LINE_ID to a registered bytes32 line id (0x-prefixed)',
    });
    return;
  }
  const a138 = omnlComplianceCore138();
  const a651940 = process.env.OMNL_COMPLIANCE_CORE_651940;
  const out: Record<string, unknown> = {
    lineId: line,
    configured138: Boolean(a138),
    configured651940: Boolean(a651940),
  };
  try {
    if (a138) {
      out.compliance138 = await fetchOmnlCompliance(138, line, a138);
    }
    if (a651940) {
      out.compliance651940 = await fetchOmnlCompliance(651940, line, a651940);
    }
    if (a138 && a651940 && out.compliance138 && out.compliance651940) {
      const x = out.compliance138 as { r: string; reportingCompliant: boolean };
      const y = out.compliance651940 as { r: string; reportingCompliant: boolean };
      out.rSynced = x.r === y.r;
      out.reportingSynced =
        x.reportingCompliant === y.reportingCompliant && x.r === y.r;
    }
    res.json(out);
  } catch (e) {
    const msg = e instanceof Error ? e.message : String(e);
    res.status(500).json({ ...out, error: msg });
  }
});

export default router;
