import { describe, it, expect, vi, beforeEach } from 'vitest';

// Mock db module
vi.mock('./db', () => ({
  getRequestById: vi.fn(),
  updateRequestSalles: vi.fn(),
  createRequestHistory: vi.fn(),
  getAssociationById: vi.fn(),
  getApprovedReservations: vi.fn(),
}));

// Mock notification
vi.mock('./_core/notification', () => ({
  notifyOwner: vi.fn().mockResolvedValue(true),
}));

import * as db from './db';
import { notifyOwner } from './_core/notification';

describe('Calendrier des réservations - Backend', () => {
  beforeEach(() => {
    vi.clearAllMocks();
  });

  describe('getApprovedReservations', () => {
    it('should return approved reservations with association info', async () => {
      const mockReservations = [
        {
          id: 1,
          associationId: 10,
          type: 'demande_salle',
          titre: 'Réservation Salle TOUCAN',
          status: 'validee',
          formData: JSON.stringify({
            sallesSelectionnees: ['Salle TOUCAN'],
            dateReservation: '2026-04-15',
            heureDebut: '09:00',
            heureFin: '17:00',
            motifReservation: 'AG annuelle',
          }),
          associationName: 'Association Test',
          associationEmail: 'test@asso.fr',
          createdAt: new Date('2026-03-01'),
        },
        {
          id: 2,
          associationId: 11,
          type: 'demande_salle',
          titre: 'Réservation DOJO',
          status: 'validee',
          formData: JSON.stringify({
            sallesSelectionnees: ['DOJO'],
            dateReservation: '2026-04-20',
            heureDebut: '18:00',
            heureFin: '20:00',
            motifReservation: 'Entraînement',
          }),
          associationName: 'Club Judo',
          associationEmail: 'judo@club.fr',
          createdAt: new Date('2026-03-05'),
        },
      ];

      vi.mocked(db.getApprovedReservations).mockResolvedValue(mockReservations as any);

      const result = await db.getApprovedReservations();
      expect(result).toHaveLength(2);
      expect(result[0].associationName).toBe('Association Test');
      expect(result[1].associationName).toBe('Club Judo');
    });

    it('should return empty array when no approved reservations', async () => {
      vi.mocked(db.getApprovedReservations).mockResolvedValue([]);
      const result = await db.getApprovedReservations();
      expect(result).toHaveLength(0);
    });
  });

  describe('changeSalles - logic simulation', () => {
    it('should update salles in formData correctly', async () => {
      const existingRequest = {
        id: 1,
        type: 'demande_salle',
        associationId: 10,
        status: 'validee',
        formData: JSON.stringify({
          sallesSelectionnees: ['Salle TOUCAN'],
          sallesIds: ['salle_toucan'],
          dateReservation: '2026-04-15',
          heureDebut: '09:00',
          heureFin: '17:00',
        }),
      };

      vi.mocked(db.getRequestById).mockResolvedValue(existingRequest as any);
      vi.mocked(db.updateRequestSalles).mockResolvedValue(undefined);
      vi.mocked(db.createRequestHistory).mockResolvedValue(undefined as any);
      vi.mocked(db.getAssociationById).mockResolvedValue({
        id: 10,
        nomAssociation: 'Association Test',
        emailContact: 'test@asso.fr',
      } as any);

      // Simulate the changeSalles logic
      const request = await db.getRequestById(1);
      expect(request).toBeDefined();
      expect(request!.type).toBe('demande_salle');

      const formData = JSON.parse(request!.formData!);
      const anciennesSalles = formData.sallesSelectionnees;

      // Update salles
      formData.sallesSelectionnees = ['Salle IBIS', 'Salle PELICAN'];
      formData.sallesIds = ['salle_ibis', 'salle_pelican'];

      // Add change history
      formData.historiqueChangementsSalles = [{
        date: new Date().toISOString(),
        anciennesSalles,
        nouvellesSalles: ['Salle IBIS', 'Salle PELICAN'],
        raison: 'Besoin d\'une salle plus grande',
        parAdmin: 1,
      }];

      await db.updateRequestSalles(1, JSON.stringify(formData));

      expect(db.updateRequestSalles).toHaveBeenCalledWith(
        1,
        expect.stringContaining('Salle IBIS')
      );

      // Verify the updated formData
      const updatedFormData = JSON.parse(
        vi.mocked(db.updateRequestSalles).mock.calls[0][1]
      );
      expect(updatedFormData.sallesSelectionnees).toEqual(['Salle IBIS', 'Salle PELICAN']);
      expect(updatedFormData.sallesIds).toEqual(['salle_ibis', 'salle_pelican']);
      expect(updatedFormData.historiqueChangementsSalles).toHaveLength(1);
      expect(updatedFormData.historiqueChangementsSalles[0].anciennesSalles).toEqual(['Salle TOUCAN']);
      expect(updatedFormData.historiqueChangementsSalles[0].raison).toBe('Besoin d\'une salle plus grande');
    });

    it('should create request history entry for salle change', async () => {
      vi.mocked(db.createRequestHistory).mockResolvedValue(undefined as any);

      await db.createRequestHistory({
        requestId: 1,
        action: 'changement_salle',
        ancienStatut: 'validee',
        nouveauStatut: 'validee',
        userId: 1,
        commentaire: 'Changement de salle(s) : Salle TOUCAN → Salle IBIS. Raison : Besoin d\'une salle plus grande',
      });

      expect(db.createRequestHistory).toHaveBeenCalledWith(
        expect.objectContaining({
          requestId: 1,
          action: 'changement_salle',
          commentaire: expect.stringContaining('Salle TOUCAN'),
        })
      );
    });

    it('should reject change for non-salle requests', async () => {
      const nonSalleRequest = {
        id: 2,
        type: 'subvention_fonctionnement',
        associationId: 10,
        status: 'validee',
        formData: '{}',
      };

      vi.mocked(db.getRequestById).mockResolvedValue(nonSalleRequest as any);

      const request = await db.getRequestById(2);
      expect(request!.type).not.toBe('demande_salle');
      // In the actual procedure, this would throw a TRPCError
    });

    it('should send notification to association on salle change', async () => {
      vi.mocked(db.getAssociationById).mockResolvedValue({
        id: 10,
        nomAssociation: 'Association Test',
        emailContact: 'test@asso.fr',
      } as any);

      const association = await db.getAssociationById(10);
      expect(association).toBeDefined();

      await notifyOwner({
        title: 'Changement de salle pour votre réservation',
        content: `Bonjour ${association!.nomAssociation}, votre salle a été modifiée.`,
      });

      expect(notifyOwner).toHaveBeenCalledWith(
        expect.objectContaining({
          title: 'Changement de salle pour votre réservation',
          content: expect.stringContaining('Association Test'),
        })
      );
    });

    it('should preserve change history across multiple changes', () => {
      const formData = {
        sallesSelectionnees: ['Salle PELICAN'],
        sallesIds: ['salle_pelican'],
        historiqueChangementsSalles: [
          {
            date: '2026-03-10T10:00:00Z',
            anciennesSalles: ['Salle TOUCAN'],
            nouvellesSalles: ['Salle IBIS'],
            raison: 'Premier changement',
            parAdmin: 1,
          },
          {
            date: '2026-03-11T14:00:00Z',
            anciennesSalles: ['Salle IBIS'],
            nouvellesSalles: ['Salle PELICAN'],
            raison: 'Deuxième changement',
            parAdmin: 1,
          },
        ],
      };

      expect(formData.historiqueChangementsSalles).toHaveLength(2);
      expect(formData.historiqueChangementsSalles[0].anciennesSalles).toEqual(['Salle TOUCAN']);
      expect(formData.historiqueChangementsSalles[1].nouvellesSalles).toEqual(['Salle PELICAN']);
      expect(formData.sallesSelectionnees).toEqual(['Salle PELICAN']);
    });
  });
});
