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 {
  buildSettlementTerminalContext,
  buildFullTerminalPackage,
  getTerminalArtifact,
  renderTerminalScreen,
  type TerminalArtifact,
  type TerminalScreenMode,
} from '../../services/omnl-settlement-terminal';
import {
  listTerminalConnectionIds,
  loadTerminalConnection,
  buildConnectionPublicSummary,
  connectionAlchemyApiKey,
} from '../../services/omnl-terminal-connection';
import {
  alchemyJsonRpc,
  alchemyTokenPricesByAddress,
  alchemyPrepareErc20Transfer,
  alchemyConfigured,
  type AlchemyNetwork,
} from '../../services/omnl-alchemy-client';
import { appendOmnlAudit } from '../../services/omnl-audit-log';

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

function parseQueryContext(req: Request): {
  settlementRef?: string;
  officeId?: number;
  valueDate?: string;
  amountUsd?: string;
  connectionId?: string;
} {
  const officeRaw = String(req.query.officeId ?? '').trim();
  return {
    settlementRef: String(req.query.settlementRef ?? '').trim() || undefined,
    officeId: officeRaw ? parseInt(officeRaw, 10) : undefined,
    valueDate: String(req.query.valueDate ?? '').trim() || undefined,
    amountUsd: String(req.query.amountUsd ?? '').trim() || undefined,
    connectionId: String(req.query.connectionId ?? '').trim() || undefined,
  };
}

function resolveAlchemyKey(connectionId?: string): string | undefined {
  if (!connectionId) return undefined;
  try {
    const profile = loadTerminalConnection(connectionId);
    const key = connectionAlchemyApiKey(profile);
    return key || undefined;
  } catch {
    return undefined;
  }
}

function parseArtifact(raw: string): TerminalArtifact {
  const v = raw.trim().toLowerCase();
  const allowed: TerminalArtifact[] = [
    'package',
    'proof-of-funds',
    'debit-note',
    'remittance-advice',
    'tokenization-sanitized',
    'conversion-swift-copy',
  ];
  return (allowed.includes(v as TerminalArtifact) ? v : 'package') as TerminalArtifact;
}

async function withContext(
  req: Request,
  res: Response,
  fn: (ctx: Awaited<ReturnType<typeof buildSettlementTerminalContext>>) => void | Promise<void>
): Promise<void> {
  try {
    const ctx = await buildSettlementTerminalContext(parseQueryContext(req));
    await fn(ctx);
  } catch (e) {
    const msg = e instanceof Error ? e.message : String(e);
    res.status(500).json({ error: msg });
  }
}

router.get('/omnl/terminal/status', omnlSensitiveRouteGuard, (req, res) => {
  const connectionId = String(req.query.connectionId ?? '').trim() || undefined;
  res.json({
    service: 'omnl-settlement-terminal',
    version: '1.1.0',
    alchemyConfigured: alchemyConfigured(resolveAlchemyKey(connectionId)),
    fineractEnv: Boolean(process.env.OMNL_FINERACT_BASE_URL && process.env.OMNL_FINERACT_PASSWORD),
    ui: '/omnl/terminal',
    connections: listTerminalConnectionIds(),
    activeConnectionId: connectionId,
  });
});

router.get('/omnl/terminal/connections', omnlSensitiveRouteGuard, (_req, res) => {
  const ids = listTerminalConnectionIds();
  res.json({
    connections: ids.map((id) => {
      try {
        return buildConnectionPublicSummary(loadTerminalConnection(id));
      } catch (e) {
        return { connectionId: id, error: e instanceof Error ? e.message : String(e) };
      }
    }),
  });
});

router.get('/omnl/terminal/connections/:connectionId', omnlSensitiveRouteGuard, (req, res) => {
  try {
    const profile = loadTerminalConnection(String(req.params.connectionId));
    res.json(buildConnectionPublicSummary(profile));
  } catch (e) {
    res.status(404).json({ error: e instanceof Error ? e.message : String(e) });
  }
});

router.get('/omnl/terminal/package', omnlSensitiveRouteGuard, async (req, res) => {
  await withContext(req, res, (ctx) => {
    res.json(buildFullTerminalPackage(ctx));
  });
});

router.get('/omnl/terminal/proof-of-funds', omnlSensitiveRouteGuard, async (req, res) => {
  await withContext(req, res, (ctx) => {
    res.json(getTerminalArtifact(ctx, 'proof-of-funds'));
  });
});

router.get('/omnl/terminal/debit-note', omnlSensitiveRouteGuard, async (req, res) => {
  await withContext(req, res, (ctx) => {
    res.json(getTerminalArtifact(ctx, 'debit-note'));
  });
});

router.get('/omnl/terminal/remittance-advice', omnlSensitiveRouteGuard, async (req, res) => {
  await withContext(req, res, (ctx) => {
    res.json(getTerminalArtifact(ctx, 'remittance-advice'));
  });
});

router.get('/omnl/terminal/tokenization-sanitized', omnlSensitiveRouteGuard, async (req, res) => {
  await withContext(req, res, (ctx) => {
    res.json(getTerminalArtifact(ctx, 'tokenization-sanitized'));
  });
});

router.get('/omnl/terminal/conversion-swift-copy', omnlSensitiveRouteGuard, async (req, res) => {
  await withContext(req, res, (ctx) => {
    res.json(getTerminalArtifact(ctx, 'conversion-swift-copy'));
  });
});

router.get('/omnl/terminal/screen', omnlSensitiveRouteGuard, async (req, res) => {
  await withContext(req, res, (ctx) => {
    const modeRaw = String(req.query.mode ?? 'black').trim().toLowerCase();
    const mode: TerminalScreenMode = modeRaw === 'color' ? 'color' : 'black';
    const artifact = parseArtifact(String(req.query.artifact ?? 'package'));
    const text = renderTerminalScreen(ctx, mode, artifact);
    const format = String(req.query.format ?? 'json').trim().toLowerCase();
    if (format === 'text' || format === 'plain') {
      res.type('text/plain; charset=utf-8').send(text);
      return;
    }
    res.json({
      mode,
      artifact,
      text,
      context: ctx,
    });
  });
});

router.post('/omnl/terminal/alchemy/rpc', omnlSensitiveRouteGuard, async (req, res) => {
  try {
    const network = (String(req.body?.network ?? 'eth-mainnet').trim() ||
      'eth-mainnet') as AlchemyNetwork;
    const connectionId = String(req.body?.connectionId ?? req.query.connectionId ?? '').trim();
    const apiKey = resolveAlchemyKey(connectionId || undefined);
    if (!alchemyConfigured(apiKey)) {
      res.status(503).json({ error: 'Alchemy API key unset for connection' });
      return;
    }
    const method = String(req.body?.method ?? '').trim();
    const params = Array.isArray(req.body?.params) ? req.body.params : [];
    if (!method) {
      res.status(400).json({ error: 'JSON body must include method' });
      return;
    }
    const result = await alchemyJsonRpc(network, method, params, apiKey);
    appendOmnlAudit({
      category: 'api',
      action: 'alchemy_rpc',
      metadata: { network, method },
      status: 'ok',
    });
    res.json({ network, method, result });
  } catch (e) {
    res.status(500).json({ error: e instanceof Error ? e.message : String(e) });
  }
});

router.post('/omnl/terminal/alchemy/prices', omnlSensitiveRouteGuard, async (req, res) => {
  try {
    const connectionId = String(req.body?.connectionId ?? req.query.connectionId ?? '').trim();
    const apiKey = resolveAlchemyKey(connectionId || undefined);
    if (!alchemyConfigured(apiKey)) {
      res.status(503).json({ error: 'Alchemy API key unset for connection' });
      return;
    }
    const network = (String(req.body?.network ?? 'eth-mainnet').trim() ||
      'eth-mainnet') as AlchemyNetwork;
    const addresses = Array.isArray(req.body?.addresses) ? req.body.addresses.map(String) : [];
    if (!addresses.length) {
      res.status(400).json({ error: 'JSON body must include addresses: string[]' });
      return;
    }
    const data = await alchemyTokenPricesByAddress(network, addresses, apiKey);
    appendOmnlAudit({
      category: 'api',
      action: 'alchemy_prices',
      metadata: { network, count: addresses.length },
      status: 'ok',
    });
    res.json({ network, data });
  } catch (e) {
    res.status(500).json({ error: e instanceof Error ? e.message : String(e) });
  }
});

router.post('/omnl/terminal/alchemy/tx-prep', omnlSensitiveRouteGuard, async (req, res) => {
  try {
    const connectionId = String(req.body?.connectionId ?? req.query.connectionId ?? '').trim();
    const apiKey = resolveAlchemyKey(connectionId || undefined);
    if (!alchemyConfigured(apiKey)) {
      res.status(503).json({ error: 'Alchemy API key unset for connection' });
      return;
    }
    const network = (String(req.body?.network ?? 'eth-mainnet').trim() ||
      'eth-mainnet') as AlchemyNetwork;
    const from = String(req.body?.from ?? '').trim();
    const to = String(req.body?.to ?? '').trim();
    const tokenAddress = String(req.body?.tokenAddress ?? '').trim();
    const amountHuman = String(req.body?.amount ?? req.body?.amountHuman ?? '').trim();
    const dryRun = req.body?.dryRun !== false;
    if (!from || !to || !tokenAddress || !amountHuman) {
      res.status(400).json({
        error: 'Required: from, to, tokenAddress, amount (human-readable token units)',
      });
      return;
    }
    const prep = await alchemyPrepareErc20Transfer({
      network,
      from,
      to,
      tokenAddress,
      amountHuman,
      dryRun,
      apiKeyOverride: apiKey,
    });
    appendOmnlAudit({
      category: 'api',
      action: 'alchemy_tx_prep',
      metadata: { network, from, to, tokenAddress, dryRun },
      status: 'ok',
    });
    res.json(prep);
  } catch (e) {
    res.status(500).json({ error: e instanceof Error ? e.message : String(e) });
  }
});

export default router;
