import { Request, Response, NextFunction } from 'express';
import cors from 'cors';

// Mock the express request, response, and next function
const mockRequest = (origin: string | undefined) => {
  return {
    headers: {
      origin: origin,
    },
    header: (name: string) => (name === 'Origin' ? origin : undefined)
  } as Request;
};

const mockResponse = () => {
  const headers: { [key: string]: string | string[] | undefined } = {};
  const res: Partial<Response> = {};
  res.setHeader = jest.fn((name: string, value: string | string[]) => {
    headers[name.toLowerCase()] = value;
    return res as Response;
  });
  res.getHeader = jest.fn((name: string) => headers[name.toLowerCase()]);
  res.removeHeader = jest.fn((name: string) => {
    delete headers[name.toLowerCase()];
  });
  res.status = jest.fn().mockReturnValue(res as Response);
  res.json = jest.fn().mockReturnValue(res as Response);
  return res as Response;
};

const mockNext = () => jest.fn() as NextFunction;

describe('CORS Middleware Configuration', () => {
  const OLD_ENV = process.env;

  beforeEach(() => {
    jest.resetModules(); // Most important - it clears the cache
    process.env = { ...OLD_ENV }; // Make a copy
  });

  afterAll(() => {
    process.env = OLD_ENV; // Restore old environment
  });

  test('should allow a request from the configured CORS_ORIGIN', () => {
    process.env.CORS_ORIGIN = 'http://allowed.com';
    const corsOptions = {
      origin: (origin: string | undefined, callback: (err: Error | null, allow?: boolean) => void) => {
        if (!origin || process.env.CORS_ORIGIN?.includes(origin)) {
          callback(null, true);
        } else {
          callback(new Error('Not allowed by CORS'));
        }
      },
    };
    const corsMiddleware = cors(corsOptions);
    const req = mockRequest('http://allowed.com');
    const res = mockResponse();
    const next = mockNext();

    corsMiddleware(req, res, next);

    expect(next).toHaveBeenCalledWith();
    expect(next).not.toHaveBeenCalledWith(expect.any(Error));
  });

  test('should block a request from an unlisted origin', () => {
    process.env.CORS_ORIGIN = 'http://allowed.com';
    const corsOptions = {
      origin: (origin: string | undefined, callback: (err: Error | null, allow?: boolean) => void) => {
        if (!origin || process.env.CORS_ORIGIN?.includes(origin)) {
          callback(null, true);
        } else {
          callback(new Error('Not allowed by CORS'));
        }
      },
    };
    const corsMiddleware = cors(corsOptions);
    const req = mockRequest('http://denied.com');
    const res = mockResponse();
    const next = mockNext();

    corsMiddleware(req, res, next);

    expect(next).toHaveBeenCalledWith(new Error('Not allowed by CORS'));
  });

  test('should block a cross-origin request if CORS_ORIGIN is not set (default same-origin)', () => {
    delete process.env.CORS_ORIGIN;
    const corsOptions = {
      origin: (origin: string | undefined, callback: (err: Error | null, allow?: boolean) => void) => {
        // In a real scenario, an empty CORS_ORIGIN would mean only same-origin is allowed.
        // This is simulated by checking if origin exists and is not in the (non-existent) whitelist.
        if (!origin || process.env.CORS_ORIGIN?.includes(origin)) {
          callback(null, true);
        } else {
          callback(new Error('Not allowed by CORS'));
        }
      },
    };
    const corsMiddleware = cors(corsOptions);
    const req = mockRequest('http://any-origin.com');
    const res = mockResponse();
    const next = mockNext();

    corsMiddleware(req, res, next);

    expect(next).toHaveBeenCalledWith(new Error('Not allowed by CORS'));
  });

  test('should allow a same-origin request if CORS_ORIGIN is not set', () => {
    delete process.env.CORS_ORIGIN;
    const corsOptions = {
      origin: (origin: string | undefined, callback: (err: Error | null, allow?: boolean) => void) => {
        if (!origin || process.env.CORS_ORIGIN?.includes(origin)) {
          callback(null, true);
        } else {
          callback(new Error('Not allowed by CORS'));
        }
      },
    };
    const corsMiddleware = cors(corsOptions);
    const req = mockRequest(undefined); // A same-origin request has no Origin header
    const res = mockResponse();
    const next = mockNext();

    corsMiddleware(req, res, next);

    expect(next).toHaveBeenCalledWith();
    expect(next).not.toHaveBeenCalledWith(expect.any(Error));
  });
});
