import { createServer, type Server, type IncomingMessage, type ServerResponse } from 'http';
import { URL } from 'url';
import {
  verifyWebhookSignature,
  deriveIdempotencyKey,
  IdempotencyStore,
  createCorrelationContext,
  CORRELATION_ID_HEADER,
  REQUEST_ID_HEADER,
} from '@dbis/integration-foundation';
import type { InboundMessage } from '../types';
import { detectInboundFormat } from '../parsers/inboundParser';

export type WebhookServerOptions = {
  port: number;
  path: string;
  healthPath?: string;
  authBearer?: string;
  webhookSecret?: string;
  webhookSignatureHeader?: string;
  onMessage: (msg: InboundMessage) => Promise<void>;
};

const idempotencyStore = new IdempotencyStore();

function readBody(req: IncomingMessage): Promise<string> {
  return new Promise((resolve, reject) => {
    const chunks: Buffer[] = [];
    req.on('data', (c) => chunks.push(c));
    req.on('end', () => resolve(Buffer.concat(chunks).toString('utf8')));
    req.on('error', reject);
  });
}

function unauthorized(res: ServerResponse, reason = 'unauthorized'): void {
  res.statusCode = 401;
  res.setHeader('Content-Type', 'application/json');
  res.end(JSON.stringify({ error: reason }));
}

function conflict(res: ServerResponse): void {
  res.statusCode = 409;
  res.setHeader('Content-Type', 'application/json');
  res.end(JSON.stringify({ error: 'duplicate idempotency key' }));
}

export function createWebhookServer(options: WebhookServerOptions): Server {
  const healthPath = options.healthPath ?? '/health';
  const sigHeader = options.webhookSignatureHeader ?? 'x-hybx-signature';

  return createServer((req, res) => {
    void (async () => {
      try {
        const url = new URL(req.url ?? '/', `http://${req.headers.host ?? 'localhost'}`);
        if (req.method === 'GET' && url.pathname === healthPath) {
          res.statusCode = 200;
          res.setHeader('Content-Type', 'application/json');
          res.end(JSON.stringify({ status: 'ok', service: 'swift-listener' }));
          return;
        }

        if (url.pathname !== options.path) {
          res.statusCode = 404;
          res.end(JSON.stringify({ error: 'not found' }));
          return;
        }

        if (req.method !== 'POST') {
          res.statusCode = 405;
          res.end(JSON.stringify({ error: 'method not allowed' }));
          return;
        }

        const raw = await readBody(req);

        if (options.webhookSecret) {
          const sig = String(req.headers[sigHeader] ?? req.headers['x-webhook-signature'] ?? '');
          if (!verifyWebhookSignature({ secret: options.webhookSecret, payload: raw, signature: sig })) {
            unauthorized(res, 'invalid webhook signature');
            return;
          }
        } else if (options.authBearer) {
          const auth = String(req.headers.authorization ?? '');
          const token = auth.startsWith('Bearer ') ? auth.slice(7) : '';
          if (token !== options.authBearer) {
            unauthorized(res);
            return;
          }
        }

        const idempotencyHeader = String(req.headers['x-idempotency-key'] ?? '');
        if (idempotencyHeader) {
          const key = deriveIdempotencyKey({
            tenantId: 'swift-listener',
            entityId: 'webhook',
            operation: 'inbound',
            resourceId: idempotencyHeader,
          });
          if (!idempotencyStore.record(key)) {
            conflict(res);
            return;
          }
        }

        const correlation = createCorrelationContext({
          correlationId: String(req.headers[CORRELATION_ID_HEADER] ?? ''),
          requestId: String(req.headers[REQUEST_ID_HEADER] ?? req.headers['x-request-id'] ?? ''),
        });

        const contentType = String(req.headers['content-type'] ?? '');
        let format = detectInboundFormat(raw);
        if (contentType.includes('xml')) format = 'iso20022';
        if (contentType.includes('json') && format !== 'swift_fin') format = 'json_broadcast';

        await options.onMessage({
          source: `webhook:${correlation.requestId}`,
          format,
          payload: raw,
          metadata: {
            contentType,
            path: url.pathname,
            correlationId: correlation.correlationId,
            requestId: correlation.requestId,
          },
        });

        res.statusCode = 202;
        res.setHeader('Content-Type', 'application/json');
        res.setHeader(CORRELATION_ID_HEADER, correlation.correlationId);
        res.setHeader(REQUEST_ID_HEADER, correlation.requestId);
        res.end(JSON.stringify({ accepted: true, correlationId: correlation.correlationId }));
      } catch (e) {
        res.statusCode = 500;
        res.setHeader('Content-Type', 'application/json');
        res.end(JSON.stringify({ error: e instanceof Error ? e.message : String(e) }));
      }
    })();
  }).listen(options.port);
}
