import { AuthService } from '#services/auth/authService';
import { PasswordService } from '#services/auth/passwordService';
import { TokenService } from '#services/auth/tokenService';
import { GenericError } from '#interfaces/error/generic';
import { createUser, createUserAuth, createCompleteUser } from '@factories/user.factory';
import { hashTestPassword, verifyTestPassword } from '@helpers/auth';

// Mock dependencies
jest.mock('#services/database/sequelize/sequelizeService');
jest.mock('#utils/logger');
// Prevent model-initialization chain (provider.ts → initExport.ts → initModels)
jest.mock('#providers/UserProvider', () => ({ UserProvider: jest.fn() }));
jest.mock('#providers/PasswordResetTokenProvider', () => ({ PasswordResetTokenProvider: jest.fn() }));

const mockedSequelize = jest.requireMock('#services/database/sequelize/sequelizeService');

describe('AuthService', () => {
	beforeEach(() => {
		jest.clearAllMocks();
	});

	describe('hashPassword', () => {
		it('should hash password correctly', async () => {
			const password = 'TestPassword123!';
			const hash = await PasswordService.hashPassword(password);

			expect(hash).toBeDefined();
			expect(hash).not.toBe(password);
			expect(await verifyTestPassword(password, hash)).toBe(true);
		});
	});

	describe('verifyPassword', () => {
		it('should return true for correct password', async () => {
			const password = 'TestPassword123!';
			const hash = await hashTestPassword(password);

			const result = await PasswordService.verifyPassword(password, hash);
			expect(result).toBe(true);
		});

		it('should return false for incorrect password', async () => {
			const password = 'TestPassword123!';
			const wrongPassword = 'WrongPassword123!';
			const hash = await hashTestPassword(password);

			const result = await PasswordService.verifyPassword(wrongPassword, hash);
			expect(result).toBe(false);
		});
	});

	describe('getAccessTokenCookieOptions', () => {
		it('should return HttpOnly cookie options', () => {
			const options = TokenService.getAccessTokenCookieOptions();

			expect(options.httpOnly).toBe(true);
			expect(options.sameSite).toBe('lax');
			expect(options.path).toBe('/api');
			expect(options.maxAge).toBeGreaterThan(0);
		});
	});

	describe('getRefreshTokenCookieOptions', () => {
		it('should return HttpOnly cookie options with refresh path', () => {
			const options = TokenService.getRefreshTokenCookieOptions();

			expect(options.httpOnly).toBe(true);
			expect(options.sameSite).toBe('lax');
			expect(options.path).toBe('/api/v1/auth/refresh');
			expect(options.maxAge).toBeGreaterThan(0);
		});
	});

	describe('generateAccessToken', () => {
		it('should generate valid JWT token', () => {
			const user = createUser();
			const token = TokenService.generateAccessToken(user as any);

			expect(token).toBeDefined();
			expect(typeof token).toBe('string');
			expect(token.split('.')).toHaveLength(3); // JWT structure
		});
	});

	describe('generateRefreshToken', () => {
		it('should generate valid refresh token', () => {
			const user = createUser();
			const token = TokenService.generateRefreshToken(user as any);

			expect(token).toBeDefined();
			expect(typeof token).toBe('string');
		});
	});

	describe('verifyAccessToken', () => {
		it('should verify valid token', () => {
			const user = createUser();
			const token = TokenService.generateAccessToken(user as any);

			const decoded = TokenService.verifyAccessToken(token);

			expect(decoded.id).toBe(user.id);
			expect(decoded.email).toBe(user.email);
		});

		it('should throw error for invalid token', () => {
			expect(() => {
				TokenService.verifyAccessToken('invalid-token');
			}).toThrow(GenericError);
		});
	});

	describe('verifyRefreshToken', () => {
		it('should verify valid refresh token', () => {
			const user = createUser();
			const token = TokenService.generateRefreshToken(user as any);

			const decoded = TokenService.verifyRefreshToken(token);

			expect(decoded.id).toBe(user.id);
			expect(decoded.type).toBe('refresh');
		});

		it('should throw error for non-refresh token', () => {
			const user = createUser();
			const accessToken = TokenService.generateAccessToken(user as any);

			expect(() => {
				TokenService.verifyRefreshToken(accessToken);
			}).toThrow(GenericError);
		});
	});

	describe('parseTimeToSeconds', () => {
		it('should parse minutes format', () => {
			const seconds = TokenService.parseTimeToSeconds('15m');
			expect(seconds).toBe(900);
		});

		it('should parse hours format', () => {
			const seconds = TokenService.parseTimeToSeconds('2h');
			expect(seconds).toBe(7200);
		});

		it('should parse days format', () => {
			const seconds = TokenService.parseTimeToSeconds('7d');
			expect(seconds).toBe(604800);
		});
	});
});
