import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
import { NextRequest } from 'next/server';

import { middleware } from '../../middleware';

describe('Unit — middleware', () => {
    const originalFetch = globalThis.fetch;
    const originalConsoleLog = console.log;
    const originalConsoleError = console.error;

    beforeEach(() => {
        process.env.FRONTEND_ORIGIN = 'http://localhost:3000';
        globalThis.fetch = vi.fn();
        console.log = vi.fn();
        console.error = vi.fn();
    });

    it('lets non-API requests pass through', async () => {
        const request = new NextRequest('http://localhost/');

        const response = await middleware(request);

        expect(response.status).toBe(200);
    });

    it('returns CORS preflight response for OPTIONS', async () => {
        const request = new NextRequest('http://localhost/api/auth/login', { method: 'OPTIONS' });

        const response = await middleware(request);

        expect(response.status).toBe(204);
        expect(response.headers.get('Access-Control-Allow-Origin')).toBe('http://localhost:3000');
        expect(response.headers.get('Access-Control-Allow-Credentials')).toBe('true');
    });

    it('proxies API requests and adds CORS headers', async () => {
        const proxiedResponse = new Response(JSON.stringify({ ok: true }), { status: 200 });
        globalThis.fetch = vi.fn().mockResolvedValue(proxiedResponse);

        const request = new NextRequest('http://localhost/api/auth/me', { method: 'GET' });

        const response = await middleware(request);

        expect(globalThis.fetch).toHaveBeenCalled();
        expect(response.status).toBe(200);
        expect(response.headers.get('Access-Control-Allow-Origin')).toBe('http://localhost:3000');
    });

    it('falls back to NextResponse.next when proxying fails', async () => {
        globalThis.fetch = vi.fn().mockRejectedValue(new Error('proxy failed'));

        const request = new NextRequest('http://localhost/api/auth/me', { method: 'GET' });

        const response = await middleware(request);

        expect(response.status).toBe(200);
        expect(response.headers.get('Access-Control-Allow-Origin')).toBe('http://localhost:3000');
    });
    it('lets health route pass through', async () => {
        const request = new NextRequest('http://localhost/api/health');

        const response = await middleware(request);

        expect(response.status).toBe(200);
        expect(globalThis.fetch).not.toHaveBeenCalled();
    });

    it('continues when request logging fails', async () => {
        console.log = vi.fn().mockImplementation(() => {
            throw new Error('log failed');
        });

        const request = new NextRequest('http://localhost/');

        const response = await middleware(request);

        expect(response.status).toBe(200);
        expect(console.error).toHaveBeenCalledWith(
            '[middleware logger] failed to log request',
            expect.any(Error)
        );
    });

    it('continues proxied API requests and adds CORS headers', async () => {
        const request = new NextRequest('http://localhost/api/auth/me', {
            method: 'GET',
            headers: {
                'x-logger-proxy': '1',
            },
        });

        const response = await middleware(request);

        expect(response.status).toBe(200);
        expect(globalThis.fetch).not.toHaveBeenCalled();
        expect(response.headers.get('Access-Control-Allow-Origin')).toBe('http://localhost:3000');
        expect(response.headers.get('Access-Control-Allow-Headers')).toBe('Authorization,Content-Type');
        expect(response.headers.get('Access-Control-Allow-Credentials')).toBe('true');
    });

    it('continues when response logging fails', async () => {
        const proxiedResponse = new Response(JSON.stringify({ ok: true }), { status: 200 });
        globalThis.fetch = vi.fn().mockResolvedValue(proxiedResponse);

        console.log = vi
            .fn()
            .mockImplementationOnce(() => undefined)
            .mockImplementationOnce(() => {
                throw new Error('response log failed');
            });

        const request = new NextRequest('http://localhost/api/auth/me', { method: 'GET' });

        const response = await middleware(request);

        expect(response.status).toBe(200);
        expect(console.error).toHaveBeenCalledWith(
            '[middleware logger] failed to log response',
            expect.any(Error)
        );
    });

    afterEach(() => {
        globalThis.fetch = originalFetch;
        console.log = originalConsoleLog;
        console.error = originalConsoleError;
    });
});