import type { LogementShowAvailabilityEvent, LogementShowPlanningEvent } from '@/types/logement-show';
import { describe, expect, it } from 'vitest';
import { AVAILABILITY_COLORS } from '../components/availability-colors';
import {
    PLANNING_COLORS,
    availabilityToEvents,
    availabilityTooltipLines,
    calendarRange,
    planningCardTitle,
    planningToEvents,
    planningTone,
    planningTooltipLines,
} from './calendar-events';
import { addDaysIso, frenchDate, type Translate } from './format';

/** `t` de test : rend le repli français, comme une traduction absente. */
const t: Translate = (_namespace, key, fallback) => fallback ?? key;

const LABELS = { toAssign: 'À assigner', arrival: 'Arrivée' };

const reservation: LogementShowAvailabilityEvent = {
    kind: 'reservation',
    start_iso: '2026-10-08',
    end_iso: '2026-10-10',
    label: 'Paul',
    source: 'Airbnb',
    comment: null,
    blocked_by: null,
    reservation_url: '/reservations/show/1',
};

const cleaning: LogementShowPlanningEvent = {
    kind: 'departure_cleaning',
    date_iso: '2026-10-11',
    guest_name: 'Paul',
    assigned: true,
    housekeepers: ['Amel', 'Sami'],
    verified: false,
};

describe('dates ISO', () => {
    it('ajoute un jour en franchissant mois, année et 29 février', () => {
        expect(addDaysIso('2026-10-10', 1)).toBe('2026-10-11');
        expect(addDaysIso('2026-10-31', 1)).toBe('2026-11-01');
        expect(addDaysIso('2026-12-31', 1)).toBe('2027-01-01');
        expect(addDaysIso('2028-02-28', 1)).toBe('2028-02-29');
    });

    it('écrit la date à la française', () => {
        expect(frenchDate('2026-10-08')).toBe('08/10/2026');
    });

    it('la fenêtre de navigation inclut le dernier jour', () => {
        expect(calendarRange('2026-10-01', '2027-09-30')).toEqual({ start: '2026-10-01', end: '2027-10-01' });
    });
});

describe('calendrier des disponibilités', () => {
    it('la fin FullCalendar est exclusive : dernière nuit incluse + 1 jour', () => {
        const [event] = availabilityToEvents([reservation]);
        expect(event.start).toBe('2026-10-08');
        expect(event.end).toBe('2026-10-11');
        expect(event.allDay).toBe(true);
    });

    it('les réservations en barres portant le nom du voyageur, les blocages en fond de case sans texte', () => {
        const kinds = ['reservation', 'ical', 'manual_block', 'channel_block'] as const;
        const events = availabilityToEvents(kinds.map((kind) => ({ ...reservation, kind })));
        expect(events.map((event) => event.display)).toEqual(['block', 'background', 'background', 'background']);
        expect(events[0].title).toBe(reservation.label);
        for (const event of events.slice(1)) {
            expect(event.title).toBeUndefined();
        }
        // Réservation sans nom connu : barre sans texte, pas « null ».
        expect(availabilityToEvents([{ ...reservation, label: null }])[0].title).toBeUndefined();
    });

    it("chaque nature prend la couleur de l'étape « Prix et disponibilité »", () => {
        const kinds = ['reservation', 'ical', 'manual_block', 'channel_block'] as const;
        const events = availabilityToEvents(kinds.map((kind) => ({ ...reservation, kind })));
        expect(events.map((event) => event.color)).toEqual(kinds.map((kind) => AVAILABILITY_COLORS[kind]));
        expect(AVAILABILITY_COLORS).toEqual({ reservation: '#e11d48', ical: '#0f766e', manual_block: '#475569', channel_block: '#94a3b8' });
    });

    it('infobulle d’une réservation : voyageur, canal, arrivée → départ et nuits', () => {
        expect(availabilityTooltipLines(reservation, 'fr', t)).toEqual(['Paul', 'Airbnb', '08/10/2026 → 11/10/2026 · 3 nuits']);
    });

    it('infobulle d’un blocage manuel : nature, auteur et commentaire', () => {
        const block = { ...reservation, kind: 'manual_block' as const, label: null, source: null, blocked_by: 'Farouk', comment: 'Travaux' };
        expect(availabilityTooltipLines(block, 'fr', t)).toEqual([
            'Bloqué (manuel)',
            '08/10/2026 → 11/10/2026 · 3 nuits',
            'Bloqué par : Farouk',
            'Commentaire : Travaux',
        ]);
    });

    it('le blocage côté canal ne nomme aucun fournisseur', () => {
        const block = { ...reservation, kind: 'channel_block' as const, label: null, source: null, start_iso: '2026-10-08', end_iso: '2026-10-08' };
        expect(availabilityTooltipLines(block, 'fr', t)).toEqual(['Non dispo (canal)', '08/10/2026 → 09/10/2026 · 1 nuit']);
    });
});

describe('planning ménage', () => {
    it('vert assigné, ambre à assigner, neutre pour une arrivée', () => {
        expect(planningTone(cleaning)).toBe('assigned');
        expect(planningTone({ ...cleaning, assigned: false, housekeepers: [] })).toBe('unassigned');
        expect(planningTone({ ...cleaning, kind: 'arrival', assigned: false })).toBe('arrival');

        const events = planningToEvents(
            [cleaning, { ...cleaning, assigned: false, housekeepers: [] }, { ...cleaning, kind: 'arrival', assigned: false }],
            LABELS,
        );
        expect(events.map((event) => event.color)).toEqual([PLANNING_COLORS.assigned, PLANNING_COLORS.unassigned, PLANNING_COLORS.arrival]);
    });

    it('une carte par passage, le ménage avant l’arrivée', () => {
        const [departure, arrival] = planningToEvents([cleaning, { ...cleaning, kind: 'arrival', assigned: false }], LABELS);
        expect(departure.start).toBe('2026-10-11');
        expect(departure.end).toBeUndefined();
        expect(departure.display).toBe('block');
        expect((departure.extendedProps as { order: number }).order).toBeLessThan((arrival.extendedProps as { order: number }).order);
    });

    it('la carte d’un ménage porte le prénom de l’équipe, sinon « À assigner » ; l’arrivée son libellé', () => {
        expect(planningCardTitle({ ...cleaning, housekeepers: ['Amel Ben Ali', 'Sami Trabelsi'] }, LABELS)).toBe('Amel, Sami');
        expect(planningCardTitle({ ...cleaning, assigned: false, housekeepers: [] }, LABELS)).toBe('À assigner');
        expect(planningCardTitle({ ...cleaning, kind: 'arrival', assigned: false, housekeepers: [] }, LABELS)).toBe('Arrivée');
    });

    it('infobulle d’un ménage : nature, voyageur, équipe, vérifié', () => {
        expect(planningTooltipLines({ ...cleaning, verified: true }, t)).toEqual([
            'Ménage de départ · 11/10/2026',
            'Paul',
            'Assigné à : Amel, Sami',
            'Vérifié',
        ]);
        expect(planningTooltipLines({ ...cleaning, kind: 'mid_stay_cleaning', assigned: false, housekeepers: [] }, t)).toEqual([
            'Ménage en cours de séjour · 11/10/2026',
            'Paul',
            "Personne n'est assigné",
        ]);
    });

    it('infobulle d’une arrivée : ni équipe ni vérification', () => {
        expect(planningTooltipLines({ ...cleaning, kind: 'arrival', assigned: false, housekeepers: [], verified: true }, t)).toEqual([
            'Arrivée · 11/10/2026',
            'Paul',
        ]);
    });
});
