import type { Request } from 'express';
import { requireCapability } from '@/shared/solacenet/sdk';
import { DbisError } from '@/shared/types';
import { recordGatewayRailsAudit } from './gateway-rails-audit';

/**
 * When SOLACENET_GATEWAY_RAILS_ENFORCE=1|true, rail mutating endpoints require
 * SolaceNet capability `gateway-microservices` for the resolved tenant.
 * Default is off so existing integrations keep working until explicitly enabled.
 */
export function gatewayRailsEnforcementEnabled(): boolean {
	const v = process.env.SOLACENET_GATEWAY_RAILS_ENFORCE;
	return v === '1' || v === 'true';
}

export function resolveGatewayTenantId(req: Request): string {
	const h = req.headers['x-tenant-id'];
	const fromHeader = typeof h === 'string' ? h : Array.isArray(h) ? h[0] : '';
	const body = req.body && typeof req.body === 'object' ? (req.body as Record<string, unknown>) : {};
	const fromBody = typeof body.tenantId === 'string' ? body.tenantId : '';
	return (
		fromBody ||
		fromHeader ||
		process.env.SOLACENET_DEFAULT_TENANT_ID ||
		'system'
	);
}

function gatewayRailsAdapterIdFromRequest(req: Request): string | undefined {
	const id = req.params?.adapterId;
	return typeof id === 'string' ? id : undefined;
}

function correlationIdFromRequest(req: Request): string | undefined {
	const c = (req as { correlationId?: string }).correlationId;
	return typeof c === 'string' ? c : undefined;
}

/**
 * HTTP routes: enforce `gateway-microservices` when SOLACENET_GATEWAY_RAILS_ENFORCE is on.
 * Emits allow/deny audit (log + optional NDJSON file).
 */
export async function maybeRequireGatewayMicroservices(req: Request): Promise<void> {
	if (!gatewayRailsEnforcementEnabled()) return;
	const tenantId = resolveGatewayTenantId(req);
	const adapterId = gatewayRailsAdapterIdFromRequest(req);
	const correlationId = correlationIdFromRequest(req);

	try {
		await requireCapability('gateway-microservices', {
			tenantId,
			capabilityId: 'gateway-microservices',
			channel: 'API',
		});
		recordGatewayRailsAudit({
			decision: 'allow',
			tenantId,
			path: req.path,
			method: req.method,
			adapterId,
			correlationId,
			ingress: 'http',
		});
	} catch (err) {
		if (err instanceof DbisError) {
			recordGatewayRailsAudit({
				decision: 'deny',
				tenantId,
				path: req.path,
				method: req.method,
				adapterId,
				correlationId,
				errorCode: err.code,
				ingress: 'http',
			});
		}
		throw err;
	}
}

export interface GatewayWorkerIngressContext {
	tenantId: string;
	/** e.g. file-drop, mq, worker */
	ingressKind: string;
	/** Free-form locator (queue name, file glob, job id) */
	detail?: string;
	adapterId?: string;
}

/**
 * Non-HTTP ingress (file workers, MQ consumers): call at start of processing when
 * SOLACENET_GATEWAY_RAILS_ENFORCE is enabled. Audit only; does not replace auth on the worker.
 */
export async function requireGatewayMicroservicesForWorker(ctx: GatewayWorkerIngressContext): Promise<void> {
	if (!gatewayRailsEnforcementEnabled()) return;
	try {
		await requireCapability('gateway-microservices', {
			tenantId: ctx.tenantId,
			capabilityId: 'gateway-microservices',
			channel: ctx.ingressKind,
		});
		recordGatewayRailsAudit({
			decision: 'allow',
			tenantId: ctx.tenantId,
			path: ctx.detail ?? ctx.ingressKind,
			method: 'WORKER',
			adapterId: ctx.adapterId,
			ingress: ctx.ingressKind,
		});
	} catch (err) {
		if (err instanceof DbisError) {
			recordGatewayRailsAudit({
				decision: 'deny',
				tenantId: ctx.tenantId,
				path: ctx.detail ?? ctx.ingressKind,
				method: 'WORKER',
				adapterId: ctx.adapterId,
				errorCode: err.code,
				ingress: ctx.ingressKind,
			});
		}
		throw err;
	}
}
