/**
 * Tests unitaires pour DailyService
 */

/* eslint-disable @typescript-eslint/no-explicit-any */
import { describe, it, expect, beforeEach, vi } from 'vitest';
import { DailyService } from '../daily';
import { prisma } from '@/services/core/database';
import {
  getPreviousWorkday,
  normalizeDate,
  getToday,
  getYesterday,
} from '@/lib/date-utils';

// Mock de prisma
vi.mock('@/services/core/database', () => ({
  prisma: {
    dailyCheckbox: {
      findMany: vi.fn(),
      findUnique: vi.fn(),
      create: vi.fn(),
      update: vi.fn(),
      delete: vi.fn(),
      aggregate: vi.fn(),
    },
  },
}));

// Mock de date-utils
vi.mock('@/lib/date-utils', () => ({
  getPreviousWorkday: vi.fn(),
  normalizeDate: vi.fn(),
  formatDateForAPI: vi.fn(),
  getToday: vi.fn(),
  getYesterday: vi.fn(),
}));

describe('DailyService', () => {
  let service: DailyService;
  const mockUserId = 'user-123';
  const mockDate = new Date('2024-01-15');

  beforeEach(() => {
    vi.clearAllMocks();
    service = new DailyService();
    vi.mocked(normalizeDate).mockImplementation((date) => date);
    vi.mocked(getPreviousWorkday).mockReturnValue(new Date('2024-01-14'));
    vi.mocked(getToday).mockReturnValue(mockDate);
    vi.mocked(getYesterday).mockReturnValue(new Date('2024-01-14'));
  });

  describe('getDailyView', () => {
    it('devrait récupérer la vue daily pour une date', async () => {
      const yesterdayCheckboxes: any[] = [];
      const todayCheckboxes: any[] = [];

      vi.mocked(prisma.dailyCheckbox.findMany)
        .mockResolvedValueOnce(yesterdayCheckboxes)
        .mockResolvedValueOnce(todayCheckboxes);

      const view = await service.getDailyView(mockDate, mockUserId);

      expect(view.date).toEqual(mockDate);
      expect(view.yesterday).toEqual(yesterdayCheckboxes);
      expect(view.today).toEqual(todayCheckboxes);
    });
  });

  describe('getCheckboxesByDate', () => {
    it('devrait récupérer les checkboxes pour une date', async () => {
      const mockCheckboxes = [
        {
          id: 'checkbox-1',
          date: mockDate,
          text: 'Checkbox 1',
          isChecked: false,
          type: 'task',
          order: 0,
          taskId: null,
          userId: mockUserId,
          task: null,
          user: { id: mockUserId },
          createdAt: new Date(),
          updatedAt: new Date(),
        },
      ];

      vi.mocked(prisma.dailyCheckbox.findMany).mockResolvedValue(
        mockCheckboxes as any
      );

      const checkboxes = await service.getCheckboxesByDate(
        mockDate,
        mockUserId
      );

      expect(checkboxes).toBeDefined();
      expect(prisma.dailyCheckbox.findMany).toHaveBeenCalledWith({
        where: {
          date: mockDate,
          userId: mockUserId,
        },
        include: {
          task: {
            include: {
              taskTags: {
                include: {
                  tag: true,
                },
              },
              primaryTag: true,
            },
          },
          user: true,
        },
        orderBy: { order: 'asc' },
      });
    });
  });

  describe('addCheckbox', () => {
    it('devrait ajouter une checkbox', async () => {
      const mockCheckbox = {
        id: 'checkbox-1',
        date: mockDate,
        text: 'New checkbox',
        isChecked: false,
        type: 'task',
        order: 0,
        taskId: null,
        userId: mockUserId,
        task: null,
        user: { id: mockUserId },
        createdAt: new Date(),
        updatedAt: new Date(),
      };

      vi.mocked(prisma.dailyCheckbox.aggregate).mockResolvedValue({
        _max: { order: null },
      } as any);
      vi.mocked(prisma.dailyCheckbox.create).mockResolvedValue(
        mockCheckbox as any
      );

      const checkbox = await service.addCheckbox({
        date: mockDate,
        text: 'New checkbox',
        userId: mockUserId,
      });

      expect(checkbox).toBeDefined();
      expect(prisma.dailyCheckbox.create).toHaveBeenCalled();
    });

    it("devrait utiliser l'ordre spécifié", async () => {
      const mockCheckbox = {
        id: 'checkbox-1',
        date: mockDate,
        text: 'New checkbox',
        isChecked: false,
        type: 'task',
        order: 5,
        taskId: null,
        userId: mockUserId,
        task: null,
        user: { id: mockUserId },
        createdAt: new Date(),
        updatedAt: new Date(),
      };

      vi.mocked(prisma.dailyCheckbox.create).mockResolvedValue(
        mockCheckbox as any
      );

      await service.addCheckbox({
        date: mockDate,
        text: 'New checkbox',
        userId: mockUserId,
        order: 5,
      });

      expect(prisma.dailyCheckbox.create).toHaveBeenCalledWith(
        expect.objectContaining({
          data: expect.objectContaining({
            order: 5,
          }),
        })
      );
    });
  });

  describe('updateCheckbox', () => {
    it('devrait mettre à jour une checkbox', async () => {
      const mockCheckbox = {
        id: 'checkbox-1',
        date: mockDate,
        text: 'Updated checkbox',
        isChecked: true,
        type: 'task',
        order: 0,
        taskId: null,
        userId: mockUserId,
        task: null,
        user: { id: mockUserId },
        createdAt: new Date(),
        updatedAt: new Date(),
      };

      vi.mocked(prisma.dailyCheckbox.update).mockResolvedValue(
        mockCheckbox as any
      );

      const checkbox = await service.updateCheckbox('checkbox-1', {
        text: 'Updated checkbox',
        isChecked: true,
      });

      expect(checkbox).toBeDefined();
      expect(prisma.dailyCheckbox.update).toHaveBeenCalled();
    });
  });

  describe('toggleCheckbox', () => {
    it("devrait toggle l'état d'une checkbox", async () => {
      const existingCheckbox = {
        id: 'checkbox-1',
        isChecked: false,
      };

      const updatedCheckbox = {
        id: 'checkbox-1',
        date: mockDate,
        text: 'Checkbox',
        isChecked: true,
        type: 'task',
        order: 0,
        taskId: null,
        userId: mockUserId,
        task: null,
        user: { id: mockUserId },
        createdAt: new Date(),
        updatedAt: new Date(),
      };

      vi.mocked(prisma.dailyCheckbox.findUnique).mockResolvedValue(
        existingCheckbox as any
      );
      vi.mocked(prisma.dailyCheckbox.update).mockResolvedValue(
        updatedCheckbox as any
      );

      const checkbox = await service.toggleCheckbox('checkbox-1');

      expect(checkbox.isChecked).toBe(true);
      expect(prisma.dailyCheckbox.update).toHaveBeenCalledWith(
        expect.objectContaining({
          data: expect.objectContaining({
            isChecked: true,
          }),
        })
      );
    });

    it("devrait lancer une erreur si la checkbox n'existe pas", async () => {
      vi.mocked(prisma.dailyCheckbox.findUnique).mockResolvedValue(null);

      await expect(service.toggleCheckbox('non-existent')).rejects.toThrow(
        'non trouvée'
      );
    });
  });

  describe('deleteCheckbox', () => {
    it('devrait supprimer une checkbox', async () => {
      const existingCheckbox = {
        id: 'checkbox-1',
        date: mockDate,
        text: 'Checkbox',
        isChecked: false,
        type: 'task',
        order: 0,
        taskId: null,
        userId: mockUserId,
        createdAt: new Date(),
        updatedAt: new Date(),
      };

      vi.mocked(prisma.dailyCheckbox.findUnique).mockResolvedValue(
        existingCheckbox as any
      );
      vi.mocked(prisma.dailyCheckbox.delete).mockResolvedValue({} as any);

      await service.deleteCheckbox('checkbox-1');

      expect(prisma.dailyCheckbox.delete).toHaveBeenCalledWith({
        where: { id: 'checkbox-1' },
      });
    });

    it("devrait lancer une erreur si la checkbox n'existe pas", async () => {
      vi.mocked(prisma.dailyCheckbox.findUnique).mockResolvedValue(null);

      await expect(service.deleteCheckbox('non-existent')).rejects.toThrow(
        'non trouvée'
      );
    });
  });
});
