import type { Request, Response } from 'express';
import { omnlComplianceGateMiddleware } from './omnl-compliance-gate';
import { omnlIdempotencyMiddleware } from './omnl-idempotency';

function mockRes(): Response & { statusCode?: number; body?: unknown; headers: Record<string, string> } {
  const headers: Record<string, string> = {};
  const res = {
    headers,
    statusCode: 200,
    body: undefined as unknown,
    setHeader(name: string, value: string) {
      headers[name.toLowerCase()] = value;
    },
    status(code: number) {
      this.statusCode = code;
      return this;
    },
    json(payload: unknown) {
      this.body = payload;
      return this;
    },
  };
  return res as Response & typeof res;
}

function mockReq(overrides: Partial<Request> & { path?: string; method?: string } = {}): Request {
  return {
    method: 'POST',
    path: '/api/v1/omnl/iso20022/messages',
    headers: {},
    body: { messageType: 'pacs.008', payload: '<xml/>' },
    ...overrides,
  } as Request;
}

describe('omnlComplianceGateMiddleware', () => {
  const oldGate = process.env.OMNL_COMPLIANCE_GATE;

  afterEach(() => {
    if (oldGate === undefined) delete process.env.OMNL_COMPLIANCE_GATE;
    else process.env.OMNL_COMPLIANCE_GATE = oldGate;
  });

  it('passes through when OMNL_COMPLIANCE_GATE is unset', async () => {
    delete process.env.OMNL_COMPLIANCE_GATE;
    const next = jest.fn();
    await omnlComplianceGateMiddleware(mockReq(), mockRes(), next);
    expect(next).toHaveBeenCalled();
  });

  it('sets compliance headers and passes when gate enabled', async () => {
    process.env.OMNL_COMPLIANCE_GATE = '1';
    const next = jest.fn();
    const res = mockRes();
    await omnlComplianceGateMiddleware(mockReq(), res, next);
    expect(res.headers['x-omnl-compliance-decision']).toBeDefined();
    expect(res.headers['x-omnl-audit-event-id']).toBeDefined();
    expect(next).toHaveBeenCalled();
  });

  it('skips non-POST methods', async () => {
    process.env.OMNL_COMPLIANCE_GATE = '1';
    const next = jest.fn();
    await omnlComplianceGateMiddleware(mockReq({ method: 'GET' }), mockRes(), next);
    expect(next).toHaveBeenCalled();
  });
});

describe('omnlIdempotencyMiddleware', () => {
  it('passes when X-Idempotency-Key is absent', () => {
    const next = jest.fn();
    omnlIdempotencyMiddleware(mockReq(), mockRes(), next);
    expect(next).toHaveBeenCalled();
  });

  it('returns 409 on duplicate idempotency key', () => {
    const req = mockReq({
      headers: { 'x-idempotency-key': 'idem-test-duplicate-key' },
    });
    const next = jest.fn();
    omnlIdempotencyMiddleware(req, mockRes(), next);
    expect(next).toHaveBeenCalledTimes(1);

    const res = mockRes();
    const next2 = jest.fn();
    omnlIdempotencyMiddleware(req, res, next2);
    expect(res.statusCode).toBe(409);
    expect(next2).not.toHaveBeenCalled();
    expect(res.body).toMatchObject({ error: 'duplicate_idempotency_key' });
  });
});
