import { describe, it, expect, beforeEach, vi } from 'vitest';
import { GET, POST } from '../route';
import { categoryService } from '@/lib/services/categoryService';
import { getServerSession } from 'next-auth';

vi.mock('@/lib/services/categoryService', () => ({
  categoryService: {
    getCategories: vi.fn(),
    createCategory: vi.fn(),
  },
}));

vi.mock('next-auth', () => ({
  getServerSession: vi.fn(),
}));

vi.mock('../auth/[...nextauth]/route', () => ({
  authOptions: {},
}));

describe('GET /api/categories', () => {
  beforeEach(() => {
    vi.clearAllMocks();
  });

  it('debería retornar 401 si no hay sesión', async () => {
    vi.mocked(getServerSession).mockResolvedValue(null);

    const response = await GET();
    const data = await response.json();

    expect(response.status).toBe(401);
    expect(data.error).toBe('No autenticado');
  });

  it('debería retornar categorías para usuario autenticado', async () => {
    const mockSession = { user: { id: 'user1' } };
    const mockCategories = [
      {
        id: '1',
        name: 'Facturas',
        description: 'Facturas de compra',
        color: '#3B82F6',
        icon: '📄',
        createdAt: new Date(),
        updatedAt: new Date(),
        _count: { documents: 5 },
      },
      {
        id: '2',
        name: 'Recibos',
        description: null,
        color: '#10B981',
        icon: '📋',
        createdAt: new Date(),
        updatedAt: new Date(),
        _count: { documents: 3 },
      },
    ];

    vi.mocked(getServerSession).mockResolvedValue(mockSession as any);
    vi.mocked(categoryService.getCategories).mockResolvedValue(mockCategories as any);

    const response = await GET();
    const data = await response.json();

    expect(response.status).toBe(200);
    expect(data.success).toBe(true);
    expect(data.categories).toHaveLength(2);
    expect(data.categories[0].name).toBe('Facturas');
    expect(categoryService.getCategories).toHaveBeenCalled();
  });

  it('debería retornar array vacío si no hay categorías', async () => {
    const mockSession = { user: { id: 'user1' } };

    vi.mocked(getServerSession).mockResolvedValue(mockSession as any);
    vi.mocked(categoryService.getCategories).mockResolvedValue([]);

    const response = await GET();
    const data = await response.json();

    expect(response.status).toBe(200);
    expect(data.success).toBe(true);
    expect(data.categories).toHaveLength(0);
  });
});

describe('POST /api/categories', () => {
  beforeEach(() => {
    vi.clearAllMocks();
  });

  it('debería retornar 401 si no hay sesión', async () => {
    vi.mocked(getServerSession).mockResolvedValue(null);

    const response = await POST({ json: vi.fn() } as any);
    const data = await response.json();

    expect(response.status).toBe(401);
    expect(data.error).toBe('No autenticado');
  });

  it('debería retornar 400 si el nombre está vacío', async () => {
    const mockSession = { user: { id: 'user1', role: 'ADMIN' } };
    vi.mocked(getServerSession).mockResolvedValue(mockSession as any);

    const mockJson = vi.fn().mockResolvedValue({ name: '' });

    const response = await POST({ json: mockJson } as any);
    const data = await response.json();

    expect(response.status).toBe(400);
    expect(data.error).toBe('El nombre es obligatorio');
  });

  it('debería retornar 400 si el nombre es solo espacios', async () => {
    const mockSession = { user: { id: 'user1', role: 'ADMIN' } };
    vi.mocked(getServerSession).mockResolvedValue(mockSession as any);

    const mockJson = vi.fn().mockResolvedValue({ name: '   ' });

    const response = await POST({ json: mockJson } as any);
    const data = await response.json();

    expect(response.status).toBe(400);
    expect(data.error).toBe('El nombre es obligatorio');
  });

  it('debería crear categoría exitosamente para ADMIN', async () => {
    const mockSession = { user: { id: 'admin1', role: 'ADMIN' } };
    vi.mocked(getServerSession).mockResolvedValue(mockSession as any);

    const requestBody = {
      name: 'Nueva Categoría',
      description: 'Descripción de prueba',
      color: '#EF4444',
      icon: '📁',
    };

    vi.mocked(categoryService.createCategory).mockResolvedValue({
      id: 'cat1',
    });

    const response = await POST({ json: vi.fn().mockResolvedValue(requestBody) } as any);
    const data = await response.json();

    expect(response.status).toBe(201);
    expect(data.success).toBe(true);
    expect(data.categoryId).toBe('cat1');
    expect(categoryService.createCategory).toHaveBeenCalledWith(
      {
        name: 'Nueva Categoría',
        description: 'Descripción de prueba',
        color: '#EF4444',
        icon: '📁',
      },
      'ADMIN'
    );
  });

  it('debería usar valores por defecto si no se proporcionan', async () => {
    const mockSession = { user: { id: 'admin1', role: 'ADMIN' } };
    vi.mocked(getServerSession).mockResolvedValue(mockSession as any);

    const requestBody = { name: 'Categoría Simple' };

    vi.mocked(categoryService.createCategory).mockResolvedValue({
      categoryId: 'cat1',
    });

    await POST({ json: vi.fn().mockResolvedValue(requestBody) } as any);

    expect(categoryService.createCategory).toHaveBeenCalledWith(
      {
        name: 'Categoría Simple',
        description: undefined,
        color: '#3B82F6',
        icon: '📄',
      },
      'ADMIN'
    );
  });

  it('debería manejar error del service', async () => {
    const mockSession = { user: { id: 'admin1', role: 'ADMIN' } };
    vi.mocked(getServerSession).mockResolvedValue(mockSession as any);

    const requestBody = { name: 'Existente' };

    vi.mocked(categoryService.createCategory).mockRejectedValue(
      new Error('Ya existe una categoría con ese nombre')
    );

    const response = await POST({ json: vi.fn().mockResolvedValue(requestBody) } as any);

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