/**
 * @swagger
 * tags:
 *   name: SolaceNet Gateway Rails
 *   description: SolaceNet-maintained financial rail adapters (metadata, health, validate, receive)
 */

import { Router } from 'express';
import rateLimit from 'express-rate-limit';
import { v4 as uuidv4 } from 'uuid';
import { gatewayApiService } from '../edge/api-gateway.service';
import { eventStoreService } from '../data/event-store.service';
import { gatewayInboxService } from '../control/inbox.service';
import {
	createGatewayRailAdapter,
	isGatewayRailAdapterId,
	listGatewayRailAdapterIds,
} from '../adapters/gateway-adapter-registry';
import {
	gatewayRailsEnforcementEnabled,
	maybeRequireGatewayMicroservices,
} from '../rails/gateway-rails-enforcement';
import { DbisError } from '@/shared/types';

const router = Router();

// Attach correlation middleware for all gateway routes
router.use(gatewayApiService.correlationMiddleware.bind(gatewayApiService));

// Stricter budget for POST validate/receive (per-IP; stacks with global /api limiter).
if (process.env.GATEWAY_RAIL_RATE_LIMIT_IN_TEST !== '1') {
	const windowMs = parseInt(process.env.GATEWAY_RAIL_MUTATE_WINDOW_MS || '60000', 10);
	const max = parseInt(process.env.GATEWAY_RAIL_MUTATE_MAX || '120', 10);
	const railMutateLimiter = rateLimit({
		windowMs,
		max,
		standardHeaders: true,
		legacyHeaders: false,
		message: {
			success: false,
			error: {
				code: 'RATE_LIMIT_EXCEEDED',
				message: `Rail validate/receive limit: ${max} requests per ${Math.ceil(windowMs / 1000)}s.`,
			},
			timestamp: new Date(),
		},
	});
	router.use((req, res, next) => {
		if (req.method === 'POST' && /^\/rails\/[^/]+\/(validate|receive)\/?$/.test(req.path)) {
			return railMutateLimiter(req, res, next);
		}
		next();
	});
}

/**
 * @swagger
 * /api/v1/gateway/rails:
 *   get:
 *     summary: List rail adapter IDs
 *     description: SolaceNet-maintained gateway rail adapter identifiers (no live wire I/O). Uses the same API auth as other /api/v1 routes; does not call SolaceNet rail enforcement (metadata only).
 *     tags: [SolaceNet Gateway Rails]
 *     security:
 *       - SovereignToken: []
 *     responses:
 *       200:
 *         description: Adapter list and enforcement flag
 */
router.get('/rails', async (_req, res, next) => {
	try {
		return res.json({
			maintainer: 'SolaceNet',
			enforcementEnabled: gatewayRailsEnforcementEnabled(),
			adapters: listGatewayRailAdapterIds(),
			governanceDoc: 'dbis_core/docs/solacenet/RAIL_AND_PROTOCOL_GOVERNANCE.md',
		});
	} catch (error) {
		return next(error);
	}
});

/**
 * @swagger
 * /api/v1/gateway/rails/{adapterId}/health:
 *   get:
 *     summary: Rail adapter health
 *     tags: [SolaceNet Gateway Rails]
 *     security:
 *       - SovereignToken: []
 *     parameters:
 *       - in: path
 *         name: adapterId
 *         required: true
 *         schema:
 *           type: string
 *         description: e.g. dbis.adapter.swift-fin (URL-encode dots if your client requires it)
 *     responses:
 *       200:
 *         description: Health status
 *       404:
 *         description: Unknown adapter
 *       403:
 *         description: SolaceNet capability denied when SOLACENET_GATEWAY_RAILS_ENFORCE is enabled
 */
router.get('/rails/:adapterId/health', async (req, res, next) => {
	try {
		await maybeRequireGatewayMicroservices(req);
		const adapterId = req.params.adapterId;
		if (!isGatewayRailAdapterId(adapterId)) {
			return res.status(404).json({ error: 'UNKNOWN_ADAPTER', adapterId });
		}
		const adapter = createGatewayRailAdapter(adapterId)!;
		await adapter.initialize({}, undefined);
		const health = await adapter.health();
		return res.json({ adapterId, ...health });
	} catch (error) {
		if (error instanceof DbisError) {
			return res.status(403).json({ error: error.message, code: error.code });
		}
		return next(error);
	}
});

/**
 * @swagger
 * /api/v1/gateway/rails/{adapterId}/validate:
 *   post:
 *     summary: Validate canonical instruction against a rail adapter
 *     tags: [SolaceNet Gateway Rails]
 *     security:
 *       - SovereignToken: []
 *     parameters:
 *       - in: path
 *         name: adapterId
 *         required: true
 *         schema:
 *           type: string
 *     requestBody:
 *       content:
 *         application/json:
 *           schema:
 *             type: object
 *             properties:
 *               tenantId:
 *                 type: string
 *               canonicalInstruction:
 *                 type: object
 *     responses:
 *       200:
 *         description: Validation result
 */
router.post('/rails/:adapterId/validate', async (req, res, next) => {
	try {
		await maybeRequireGatewayMicroservices(req);
		const adapterId = req.params.adapterId;
		if (!isGatewayRailAdapterId(adapterId)) {
			return res.status(404).json({ error: 'UNKNOWN_ADAPTER', adapterId });
		}
		const body = req.body || {};
		const canonicalInstruction =
			body.canonicalInstruction && typeof body.canonicalInstruction === 'object'
				? (body.canonicalInstruction as Record<string, unknown>)
				: {};
		const adapter = createGatewayRailAdapter(adapterId)!;
		await adapter.initialize({}, undefined);
		const result = await adapter.validate(canonicalInstruction);
		return res.json({ adapterId, ...result });
	} catch (error) {
		if (error instanceof DbisError) {
			return res.status(403).json({ error: error.message, code: error.code });
		}
		return next(error);
	}
});

/**
 * @swagger
 * /api/v1/gateway/rails/{adapterId}/receive:
 *   post:
 *     summary: Receive / ingest rail payload (scaffold)
 *     tags: [SolaceNet Gateway Rails]
 *     security:
 *       - SovereignToken: []
 *     parameters:
 *       - in: path
 *         name: adapterId
 *         required: true
 *         schema:
 *           type: string
 *     requestBody:
 *       content:
 *         application/json:
 *           schema:
 *             type: object
 *             properties:
 *               tenantId:
 *                 type: string
 *               payload: {}
 *     responses:
 *       200:
 *         description: Receive result
 */
router.post('/rails/:adapterId/receive', async (req, res, next) => {
	try {
		await maybeRequireGatewayMicroservices(req);
		const adapterId = req.params.adapterId;
		if (!isGatewayRailAdapterId(adapterId)) {
			return res.status(404).json({ error: 'UNKNOWN_ADAPTER', adapterId });
		}
		const payload = (req.body && 'payload' in req.body ? req.body.payload : req.body) as unknown;
		const adapter = createGatewayRailAdapter(adapterId)!;
		await adapter.initialize({}, undefined);
		const result = await adapter.receive(payload);
		return res.json({ adapterId, ...result });
	} catch (error) {
		if (error instanceof DbisError) {
			return res.status(403).json({ error: error.message, code: error.code });
		}
		return next(error);
	}
});

/**
 * @swagger
 * /api/v1/gateway/instructions:
 *   post:
 *     summary: Ingest canonical instruction (scaffold)
 *     description: Accepts a JSON body, deduplicates via gateway inbox (scaffold), appends InstructionReceived to the in-memory event store, returns 202. When SOLACENET_GATEWAY_RAILS_ENFORCE is set, requires gateway-microservices for the resolved tenant (header x-tenant-id or body.tenantId).
 *     tags: [SolaceNet Gateway Rails]
 *     security:
 *       - SovereignToken: []
 *     requestBody:
 *       required: false
 *       content:
 *         application/json:
 *           schema:
 *             type: object
 *             properties:
 *               tenantId:
 *                 type: string
 *                 description: Tenant for capability check when enforcement is enabled
 *               txnId:
 *                 type: string
 *                 description: Optional idempotency / trace id (server generates if omitted)
 *               rail:
 *                 type: string
 *                 example: CANONICAL
 *               amount:
 *                 type: string
 *               currency:
 *                 type: string
 *               metadata:
 *                 type: object
 *                 additionalProperties: true
 *     responses:
 *       202:
 *         description: Accepted — instruction received (scaffold)
 *       403:
 *         description: SolaceNet capability denied when enforcement enabled
 */
router.post('/instructions', async (req, res, next) => {
	try {
		await maybeRequireGatewayMicroservices(req);
		const txnId = req.body?.txnId || `TXN-${uuidv4()}`;
		const correlationId = (req as any).correlationId || `CORR-${uuidv4()}`;

		// Ingest → Inbox (dedup)
		await gatewayInboxService.accept({
			rail: 'CANONICAL',
			payload: JSON.stringify(req.body || {}),
		});

		// Emit InstructionReceived
		eventStoreService.append({
			eventId: `EVT-${uuidv4()}`,
			txnId,
			eventType: 'InstructionReceived',
			eventTime: new Date().toISOString(),
			producerService: 'dbis.edge.api-gateway',
			payload: { correlationId, instruction: req.body || {} },
		});

		return res.status(202).json({ txnId, correlationId, status: 'RECEIVED' });
	} catch (error) {
		if (error instanceof DbisError) {
			return res.status(403).json({ error: error.message, code: error.code });
		}
		return next(error);
	}
});

/**
 * @swagger
 * /api/v1/gateway/events/replay:
 *   get:
 *     summary: Replay canonical events (scaffold, in-memory)
 *     tags: [SolaceNet Gateway Rails]
 *     security:
 *       - SovereignToken: []
 *     parameters:
 *       - in: query
 *         name: from
 *         schema:
 *           type: integer
 *           default: 0
 *         description: Start index into the in-memory event buffer
 *     responses:
 *       200:
 *         description: Event slice
 *       403:
 *         description: SolaceNet capability denied when enforcement enabled
 */
router.get('/events/replay', async (req, res, next) => {
	try {
		await maybeRequireGatewayMicroservices(req);
		const from = Number(req.query.from || 0);
		const events = eventStoreService.replay(from);
		return res.json({ count: events.length, events });
	} catch (error) {
		if (error instanceof DbisError) {
			return res.status(403).json({ error: error.message, code: error.code });
		}
		return next(error);
	}
});

export default router;

