import { RegisterCandidateData } from '../auth';

// Mock fetch globally
const mockFetch = jest.fn();
global.fetch = mockFetch;

// Mock localStorage
const localStorageMock = {
    getItem: jest.fn(),
    setItem: jest.fn(),
    removeItem: jest.fn(),
    clear: jest.fn(),
};
Object.defineProperty(window, 'localStorage', { value: localStorageMock });

// Mock config module to avoid initConfig fetching
jest.mock('../config', () => ({
    getApiUrl: jest.fn().mockReturnValue('http://test-api.com'),
    initConfig: jest.fn().mockResolvedValue({}),
    getConfig: jest.fn().mockReturnValue({}),
}));

describe('Auth Module', () => {
    let authModule: typeof import('../auth');

    beforeEach(() => {
        jest.resetModules();
        mockFetch.mockReset();
        localStorageMock.getItem.mockReset();
        localStorageMock.setItem.mockReset();
        localStorageMock.removeItem.mockReset();

        // Re-import the module fresh
        authModule = require('../auth');
    });

    describe('registerCandidate', () => {
        const validData: RegisterCandidateData = {
            name: 'John Doe',
            email: 'john@example.com',
            password: 'password123',
            username: 'johndoe',
            phone: '+5511999999999',
        };

        it('should register candidate successfully', async () => {
            const mockResponse = {
                token: 'mock-jwt-token',
                user: {
                    id: '123',
                    name: 'John Doe',
                    email: 'john@example.com',
                    roles: ['CANDIDATE'],
                },
            };

            mockFetch.mockResolvedValueOnce({
                ok: true,
                json: async () => mockResponse,
            });

            await expect(authModule.registerCandidate(validData)).resolves.not.toThrow();

            expect(mockFetch).toHaveBeenCalledWith(
                expect.stringContaining('/auth/register'),
                expect.objectContaining({
                    method: 'POST',
                    headers: { 'Content-Type': 'application/json' },
                    body: JSON.stringify(validData),
                })
            );
        });

        it('should throw error when email already exists', async () => {
            const errorResponse = { message: 'email already registered' };

            mockFetch.mockResolvedValueOnce({
                ok: false,
                status: 409,
                json: async () => errorResponse,
            });

            await expect(authModule.registerCandidate(validData)).rejects.toThrow(
                'email already registered'
            );
        });

        it('should throw error with status code when no message provided', async () => {
            mockFetch.mockResolvedValueOnce({
                ok: false,
                status: 500,
                json: async () => ({}),
            });

            await expect(authModule.registerCandidate(validData)).rejects.toThrow(
                'Erro no registro: 500'
            );
        });

        it('should handle network errors gracefully', async () => {
            mockFetch.mockRejectedValueOnce(new Error('Network error'));

            await expect(authModule.registerCandidate(validData)).rejects.toThrow(
                'Network error'
            );
        });

        it('should send all required fields in request body', async () => {
            mockFetch.mockResolvedValueOnce({
                ok: true,
                json: async () => ({}),
            });

            await authModule.registerCandidate(validData);

            const callArgs = mockFetch.mock.calls[0];
            const body = JSON.parse(callArgs[1].body);

            expect(body).toEqual({
                name: 'John Doe',
                email: 'john@example.com',
                password: 'password123',
                username: 'johndoe',
                phone: '+5511999999999',
            });
        });
    });

    describe('login', () => {
        it('should login and store user in localStorage', async () => {
            const mockResponse = {
                token: 'mock-jwt-token',
                user: {
                    id: '123',
                    name: 'Test User',
                    email: 'test@example.com',
                    roles: ['CANDIDATE'],
                    status: 'active',
                    created_at: '2023-01-01T00:00:00Z',
                },
            };

            mockFetch.mockResolvedValueOnce({
                ok: true,
                json: async () => mockResponse,
            });

            const user = await authModule.login('test@example.com', 'password123');

            expect(user).toBeDefined();
            expect(user?.email).toBe('test@example.com');
            expect(localStorageMock.setItem).toHaveBeenCalledWith(
                'job-portal-auth',
                expect.any(String)
            );
            // Token is in cookie, not in storage
            expect(localStorageMock.setItem).not.toHaveBeenCalledWith(
                'auth_token',
                expect.any(String)
            );
        });

        it('should throw error on invalid credentials', async () => {
            mockFetch.mockResolvedValueOnce({
                ok: false,
                status: 401,
            });

            await expect(
                authModule.login('test@example.com', 'wrongpassword')
            ).rejects.toThrow('Credenciais inválidas');
        });
    });

    describe('logout', () => {
        it('should remove auth data from localStorage', () => {
            authModule.logout();

            expect(localStorageMock.removeItem).toHaveBeenCalledWith('job-portal-auth');
        });
    });

    describe('refreshSession', () => {
        it('should restore user from /users/me endpoint', async () => {
            mockFetch.mockResolvedValueOnce({
                ok: true,
                json: async () => ({
                    id: '123',
                    name: 'Test User',
                    email: 'test@example.com',
                    roles: ['candidate'],
                }),
            });

            const user = await authModule.refreshSession();

            expect(user).toBeDefined();
            expect(user?.email).toBe('test@example.com');
            expect(localStorageMock.setItem).toHaveBeenCalled();
        });

        it('should clear storage on 401', async () => {
            mockFetch.mockResolvedValueOnce({ ok: false, status: 401 });

            const user = await authModule.refreshSession();

            expect(user).toBeNull();
            expect(localStorageMock.removeItem).toHaveBeenCalledWith('job-portal-auth');
        });

        it('should return null on network error', async () => {
            mockFetch.mockRejectedValueOnce(new Error('Network error'));

            const user = await authModule.refreshSession();

            expect(user).toBeNull();
        });
    });

    describe('getCurrentUser', () => {
        it('should return user from localStorage', () => {
            const storedUser = { id: '123', name: 'Test', email: 'test@test.com', role: 'candidate' };
            localStorageMock.getItem.mockReturnValueOnce(JSON.stringify(storedUser));

            const user = authModule.getCurrentUser();

            expect(user).toEqual(storedUser);
        });

        it('should return null when no user stored', () => {
            localStorageMock.getItem.mockReturnValueOnce(null);

            const user = authModule.getCurrentUser();

            expect(user).toBeNull();
        });
    });

    describe('isAdminUser', () => {
        it('should return true for admin role', () => {
            const adminUser = { id: '1', name: 'Admin', email: 'a@a.com', role: 'admin' as const, roles: ['admin'] };
            expect(authModule.isAdminUser(adminUser)).toBe(true);
        });

        it('should return true for SUPERADMIN in roles array', () => {
            const superAdmin = { id: '1', name: 'Super', email: 's@s.com', role: 'candidate' as const, roles: ['SUPERADMIN'] };
            expect(authModule.isAdminUser(superAdmin)).toBe(true);
        });

        it('should return false for regular candidate', () => {
            const candidate = { id: '1', name: 'User', email: 'u@u.com', role: 'candidate' as const, roles: ['CANDIDATE'] };
            expect(authModule.isAdminUser(candidate)).toBe(false);
        });

        it('should return false for null user', () => {
            expect(authModule.isAdminUser(null)).toBe(false);
        });
    });
});
