/**
 * Unit Tests for Login Controller
 *
 * These tests verify the login controller logic without Express/supertest overhead
 */

// Mock external dependencies before importing the controller
jest.mock('#services/auth/authService');
jest.mock('#services/auth/tokenService');
jest.mock('#utils/logger');
jest.mock('#utils/responseUtils', () => ({
	sendSuccess: (res: any, data: any, message?: string, messageEn?: string) => {
		return res.json({
			success: true,
			message: message ?? null,
			message_en: messageEn ?? null,
			responseData: data,
			trace_id: null,
			timeStamp: new Date().toISOString(),
			violations: null,
		});
	},
}));

import express, { Application } from 'express';
import { Request, Response } from 'express';
import { AuthService } from '#services/auth/authService';
import { TokenService } from '#services/auth/tokenService';

// Import controller after mocks are set up
import loginController from '#controllers/api/v1/auth/login/web';

describe('POST /auth/login/web Controller', () => {
	let app: Application;
	let mockReq: Partial<Request>;
	let mockRes: Partial<Response>;
	let mockAuthService: jest.Mocked<typeof AuthService>;
	let mockTokenService: jest.Mocked<typeof TokenService>;

	beforeAll(() => {
		app = express();
	});

	beforeEach(() => {
		jest.clearAllMocks();

		// Mock request
		mockReq = {
			body: {
				email: 'test@example.com',
				password: 'ValidPassword123!',
			},
			ip: '127.0.0.1',
			get: jest.fn().mockReturnValue('test-agent'),
		};

		// Mock response
		mockRes = {
			status: jest.fn().mockReturnThis(),
			json: jest.fn().mockReturnThis(),
			cookie: jest.fn().mockReturnThis(),
			error: jest.fn().mockReturnThis(),
		} as any;

		mockAuthService = AuthService as jest.Mocked<typeof AuthService>;
		mockTokenService = TokenService as jest.Mocked<typeof TokenService>;

		// Ensure mocked functions exist
		if (!mockAuthService.login) mockAuthService.login = jest.fn();
		if (!mockTokenService.getAccessTokenCookieOptions) mockTokenService.getAccessTokenCookieOptions = jest.fn();
		if (!mockTokenService.getRefreshTokenCookieOptions) mockTokenService.getRefreshTokenCookieOptions = jest.fn();
	});

	it('should return 200 with valid credentials', async () => {
		const now = new Date();
		mockAuthService.login.mockResolvedValue({
			accessToken: 'test-access-token',
			refreshToken: 'test-refresh-token',
			expires_in: 900,
			refresh_expires_in: 604800,
			user: {
				id: 'user-id',
				email: 'test@example.com',
				username: 'testuser',
				user_auth: { last_login_at: now } as any,
			} as any,
			session: {
				id: 'session-id',
				expires_at: new Date(Date.now() + 900000).toISOString(),
				refresh_expires_at: new Date(Date.now() + 604800000).toISOString(),
			},
			roles: [],
			permissions: [],
		});

		mockTokenService.getAccessTokenCookieOptions.mockReturnValue({
			httpOnly: true,
			secure: false,
			sameSite: 'lax',
			maxAge: 900000,
			path: '/api',
		});
		mockTokenService.getRefreshTokenCookieOptions.mockReturnValue({
			httpOnly: true,
			secure: false,
			sameSite: 'lax',
			maxAge: 604800000,
			path: '/api/v1/auth/refresh',
		});

		const controller = loginController(app);
		await (controller as any).post.handler(mockReq as Request, mockRes as Response);

		expect(mockRes.json).toHaveBeenCalled();
		expect(mockAuthService.login).toHaveBeenCalledWith(
			expect.objectContaining({
				email: 'test@example.com',
				password: 'ValidPassword123!',
			}),
		);
	});

	it('should return 401 with invalid credentials', async () => {
		mockAuthService.login.mockRejectedValue({
			message: 'Invalid email or password',
			statusCode: 401,
		});

		const controller = loginController(app);
		await (controller as any).post.handler(mockReq as Request, mockRes as Response);

		expect(mockRes.error).toHaveBeenCalled();
	});

	it('should set cookies on successful login', async () => {
		const now = new Date();
		mockAuthService.login.mockResolvedValue({
			accessToken: 'test-access-token',
			refreshToken: 'test-refresh-token',
			expires_in: 900,
			refresh_expires_in: 604800,
			user: { id: 'user-id', email: 'test@example.com', user_auth: { last_login_at: now } } as any,
			session: {
				id: 'session-id',
				expires_at: new Date(Date.now() + 900000).toISOString(),
				refresh_expires_at: new Date(Date.now() + 604800000).toISOString(),
			},
			roles: [],
			permissions: [],
		});

		mockTokenService.getAccessTokenCookieOptions.mockReturnValue({
			httpOnly: true,
			secure: false,
			sameSite: 'lax',
			maxAge: 900000,
			path: '/api',
		});
		mockTokenService.getRefreshTokenCookieOptions.mockReturnValue({
			httpOnly: true,
			secure: false,
			sameSite: 'lax',
			maxAge: 604800000,
			path: '/api/v1/auth/refresh',
		});

		const controller = loginController(app);
		await (controller as any).post.handler(mockReq as Request, mockRes as Response);

		expect(mockRes.cookie).toHaveBeenCalledWith('access_token', 'test-access-token', expect.any(Object));
		expect(mockRes.cookie).toHaveBeenCalledWith('refresh_token', 'test-refresh-token', expect.any(Object));
	});
});
