import { Router, Request, Response } from 'express';
import { omnlRateLimiter } from '../middleware/rate-limit';
import { omnlSensitiveRouteGuard, omnlRequireApiKeyInProduction } from '../middleware/omnl-guards';
import { omnlAuditMiddleware } from '../middleware/omnl-audit-middleware';
import {
  loadIpsasRegistry,
  validateJournalPairWithMatrix,
  fetchFineractGlAccounts,
  compareRegistryToFineract,
  checkFineractConnectivity,
} from '../../services/omnl-ipsas-gl';
import { loadJournalMatrix } from '../../services/omnl-journal-matrix';
import { fetchOmnlCompliance, fetchOmnlComplianceAggregated } from '../../services/omnl-compliance';
import { omnlComplianceCore138 } from '../../services/omnl-chain138-addresses';

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

/**
 * GET /omnl/ipsas/registry — full IPSAS GL registry (codes, pairs, monetary layer hints).
 */
router.get('/omnl/ipsas/registry', omnlSensitiveRouteGuard, (_req: Request, res: Response) => {
  try {
    res.json(loadIpsasRegistry());
  } catch (e) {
    const msg = e instanceof Error ? e.message : String(e);
    res.status(500).json({ error: msg });
  }
});

/**
 * GET /omnl/ipsas/matrix — journal matrix (T-001…T-008) for Fineract posting alignment.
 */
router.get('/omnl/ipsas/matrix', omnlSensitiveRouteGuard, (_req: Request, res: Response) => {
  try {
    res.json(loadJournalMatrix());
  } catch (e) {
    const msg = e instanceof Error ? e.message : String(e);
    res.status(500).json({ error: msg });
  }
});

/**
 * GET /omnl/ipsas/validate-pair?debitGlCode=&creditGlCode=
 * Ensures Debit/Credit GL codes appear in IPSAS registry or approved journal matrix.
 */
/**
 * POST /omnl/ipsas/validate-pairs — body `{ "pairs": [{ "debitGlCode", "creditGlCode" }, ...] }`
 */
router.post('/omnl/ipsas/validate-pairs', (req: Request, res: Response) => {
  try {
    const raw = (req.body as { pairs?: unknown })?.pairs;
    if (!Array.isArray(raw) || raw.length === 0) {
      res.status(400).json({ error: 'JSON body must include non-empty pairs: [{ debitGlCode, creditGlCode }, ...]' });
      return;
    }
    const registry = loadIpsasRegistry();
    let matrix = null;
    try {
      matrix = loadJournalMatrix();
    } catch {
      matrix = null;
    }
    const results = raw.map((p: unknown) => {
      const row = p as { debitGlCode?: string; creditGlCode?: string };
      const debitGlCode = String(row?.debitGlCode ?? '').trim();
      const creditGlCode = String(row?.creditGlCode ?? '').trim();
      if (!debitGlCode || !creditGlCode) {
        return {
          debitGlCode,
          creditGlCode,
          error: 'debitGlCode and creditGlCode required per pair',
        };
      }
      const v = validateJournalPairWithMatrix(registry, matrix, debitGlCode, creditGlCode);
      return {
        debitGlCode,
        creditGlCode,
        ipsasCompliantPair: v.valid,
        ipsasRef: v.ipsasRef,
        memo: v.memo,
        narrative: v.narrative,
        source: v.source,
      };
    });
    res.json({ generatedAt: new Date().toISOString(), count: results.length, results });
  } catch (e) {
    const msg = e instanceof Error ? e.message : String(e);
    res.status(500).json({ error: msg });
  }
});

router.get('/omnl/ipsas/validate-pair', (req: Request, res: Response) => {
  try {
    const debitGlCode = String(req.query.debitGlCode ?? '').trim();
    const creditGlCode = String(req.query.creditGlCode ?? '').trim();
    if (!debitGlCode || !creditGlCode) {
      res.status(400).json({ error: 'debitGlCode and creditGlCode query params required' });
      return;
    }
    const registry = loadIpsasRegistry();
    let matrix = null;
    try {
      matrix = loadJournalMatrix();
    } catch {
      matrix = null;
    }
    const v = validateJournalPairWithMatrix(registry, matrix, debitGlCode, creditGlCode);
    const debitKnown = registry.accounts.some((a) => a.glCode === debitGlCode);
    const creditKnown = registry.accounts.some((a) => a.glCode === creditGlCode);
    res.json({
      debitGlCode,
      creditGlCode,
      ipsasCompliantPair: v.valid,
      ipsasRef: v.ipsasRef,
      memo: v.memo,
      narrative: v.narrative,
      source: v.source,
      glCodesKnownInRegistry: { debit: debitKnown, credit: creditKnown },
    });
  } catch (e) {
    const msg = e instanceof Error ? e.message : String(e);
    res.status(500).json({ error: msg });
  }
});

/**
 * GET /omnl/ipsas/fineract-health — probe Fineract `/glaccounts?limit=1` (credentials from OMNL_FINERACT_*).
 */
router.get('/omnl/ipsas/fineract-health', omnlSensitiveRouteGuard, async (_req: Request, res: Response) => {
  try {
    const r = await checkFineractConnectivity();
    const configured = Boolean(
      (process.env.OMNL_FINERACT_BASE_URL || '').trim() && (process.env.OMNL_FINERACT_PASSWORD || '').trim()
    );
    res.json({
      generatedAt: new Date().toISOString(),
      configured,
      ...r,
    });
  } catch (e) {
    const msg = e instanceof Error ? e.message : String(e);
    res.status(500).json({ error: msg });
  }
});

/**
 * GET /omnl/ipsas/fineract-compare — live Fineract /glaccounts vs IPSAS registry codes (requires OMNL_FINERACT_*).
 */
router.get('/omnl/ipsas/fineract-compare', omnlSensitiveRouteGuard, async (_req: Request, res: Response) => {
  try {
    const registry = loadIpsasRegistry();
    const rows = await fetchFineractGlAccounts();
    const cmp = compareRegistryToFineract(registry, rows);
    res.json({
      ...cmp,
      fineractAccountCount: rows.length,
      ipsasCompliant: cmp.aligned,
    });
  } catch (e) {
    const msg = e instanceof Error ? e.message : String(e);
    res.status(503).json({ error: msg });
  }
});

/**
 * GET /omnl/ipsas/layer/:layer — monetary layer hint (m0_reserve | m1_liability | settlement | equity).
 */
router.get('/omnl/ipsas/layer/:layer', (req: Request, res: Response) => {
  try {
    const layer = Array.isArray(req.params.layer) ? req.params.layer[0] : req.params.layer;
    const registry = loadIpsasRegistry();
    const hint = registry.monetaryLayerHints[layer];
    if (!hint) {
      res.status(404).json({
        error: 'Unknown layer',
        known: Object.keys(registry.monetaryLayerHints),
      });
      return;
    }
    res.json({ layer, ...hint });
  } catch (e) {
    const msg = e instanceof Error ? e.message : String(e);
    res.status(500).json({ error: msg });
  }
});

/**
 * GET /omnl/ipsas/compliance-context/:lineId?aggregated=1
 * On-chain compliance snapshot + IPSAS GL hints for OMNL / Fineract alignment.
 */
router.get('/omnl/ipsas/compliance-context/:lineId', omnlSensitiveRouteGuard, async (req: Request, res: Response) => {
  try {
    const lineId = req.params.lineId as string;
    const aggregated =
      req.query.aggregated === '1' || String(req.query.aggregated || '').toLowerCase() === 'true';
    const registry = loadIpsasRegistry();
    let compliance: unknown;
    if (aggregated) {
      compliance = await fetchOmnlComplianceAggregated(lineId);
    } else {
      const addr = omnlComplianceCore138();
      if (!addr) {
        res.status(503).json({ error: 'OMNL_COMPLIANCE_CORE_138 / V2 not set' });
        return;
      }
      compliance = await fetchOmnlCompliance(138, lineId, addr);
    }
    res.json({
      compliance,
      ipsas: {
        monetaryLayerHints: registry.monetaryLayerHints,
        postingGuidance:
          'Reserve movements (M0) map to GL 1050; M1 liabilities to 2000/2100; settlement to 1000. Validate every journal with GET /api/v1/omnl/ipsas/validate-pair before POST /journalentries.',
        fineractDocs: 'https://omnl.hybxfinance.io/fineract-provider/swagger-ui/index.html',
      },
    });
  } catch (e) {
    const msg = e instanceof Error ? e.message : String(e);
    res.status(500).json({ error: msg });
  }
});

export default router;
