/**
 * Fiche logement (TL-305) — calendriers compacts de l'accordéon « Calendrier et
 * planning » : traduction PURE des événements du contrat en événements
 * FullCalendar, et lignes des infobulles. Aucun import React (tests en `node`).
 */
import type {
    LogementShowAvailabilityEvent,
    LogementShowAvailabilityKind,
    LogementShowPlanningEvent,
    LogementShowPlanningKind,
} from '@/types/logement-show';
import type { EventInput } from '@fullcalendar/core';
import { AVAILABILITY_COLORS } from '../components/availability-colors';
import { NS, addDaysIso, fill, frenchDate, nightsLabel, type Translate } from './format';

/** Fenêtre de navigation : `to_iso` est le dernier jour couvert, FullCalendar veut une fin exclusive. */
export function calendarRange(fromIso: string, toIso: string): { start: string; end: string } {
    return { start: fromIso, end: addDaysIso(toIso, 1) };
}

/* ─────────────────────────────────────────────────────────────────────────
 * Disponibilités — mêmes couleurs que l'étape « Prix et disponibilité ».
 * Réservations en barres, blocages en fond de case, sans texte : la colonne
 * est trop étroite, tout passe par l'infobulle.
 * ───────────────────────────────────────────────────────────────────────── */

export function availabilityToEvents(events: LogementShowAvailabilityEvent[]): EventInput[] {
    return events.map((event) => ({
        start: event.start_iso,
        // `end_iso` est la dernière nuit INCLUSE ; la fin FullCalendar est exclusive.
        end: addDaysIso(event.end_iso, 1),
        allDay: true,
        display: event.kind === 'reservation' ? 'block' : 'background',
        // Comme l'étape « Prix et disponibilité » : le nom du voyageur dans la barre rouge.
        ...(event.kind === 'reservation' && event.label ? { title: event.label } : {}),
        color: AVAILABILITY_COLORS[event.kind],
        extendedProps: { availability: event },
    }));
}

const AVAILABILITY_KIND_KEYS: Record<LogementShowAvailabilityKind, [string, string]> = {
    reservation: ['tip_reservation', 'Réservation'],
    ical: ['legend_ical', 'Importé via iCal'],
    manual_block: ['legend_manual_block', 'Bloqué (manuel)'],
    channel_block: ['legend_channel_block', 'Non dispo (canal)'],
};

/** Nombre de nuits d'un intervalle à dernière nuit incluse. */
function nightsBetween(startIso: string, endIso: string): number {
    return Math.round((Date.parse(`${endIso}T00:00:00Z`) - Date.parse(`${startIso}T00:00:00Z`)) / 86_400_000) + 1;
}

/** Infobulle : nature (ou voyageur), source, dates arrivée → départ et nuits, puis auteur et commentaire du blocage. */
export function availabilityTooltipLines(event: LogementShowAvailabilityEvent, locale: string, t: Translate): string[] {
    const [key, fallback] = AVAILABILITY_KIND_KEYS[event.kind];
    const heading = event.kind === 'reservation' && event.label ? event.label : t(NS, key, fallback);
    const dates = fill(t(NS, 'tip_dates', ':from → :to'), {
        from: frenchDate(event.start_iso),
        to: frenchDate(addDaysIso(event.end_iso, 1)),
    });

    return [
        heading,
        event.source,
        `${dates} · ${nightsLabel(nightsBetween(event.start_iso, event.end_iso), locale, t)}`,
        event.blocked_by ? fill(t(NS, 'tip_blocked_by', 'Bloqué par : :name'), { name: event.blocked_by }) : null,
        event.comment ? fill(t(NS, 'tip_comment', 'Commentaire : :text'), { text: event.comment }) : null,
    ].filter((line): line is string => Boolean(line));
}

/* ─────────────────────────────────────────────────────────────────────────
 * Planning ménage — une pastille par événement : vert ménage assigné, ambre
 * ménage à assigner, neutre pour une arrivée.
 * ───────────────────────────────────────────────────────────────────────── */

export type PlanningTone = 'assigned' | 'unassigned' | 'arrival';

export const PLANNING_COLORS: Record<PlanningTone, string> = {
    assigned: '#10b981',
    unassigned: '#f59e0b',
    arrival: '#64748b',
};

/** Pastilles de légende — classes écrites en entier pour que Tailwind les génère. */
export const PLANNING_SWATCH_CLASSES: Record<PlanningTone, string> = {
    assigned: 'bg-[#10b981]',
    unassigned: 'bg-[#f59e0b]',
    arrival: 'bg-[#64748b]',
};

export function planningTone(event: LogementShowPlanningEvent): PlanningTone {
    if (event.kind === 'arrival') {
        return 'arrival';
    }
    return event.assigned ? 'assigned' : 'unassigned';
}

/** Dans une case : le ménage de départ d'abord, l'arrivée qui le suit en dernier. */
const PLANNING_ORDER: Record<LogementShowPlanningKind, number> = {
    departure_cleaning: 0,
    mid_stay_cleaning: 1,
    arrival: 2,
};

/** Texte des cartes du planning, traduit par l'appelant (la fonction reste pure). */
export interface PlanningCardLabels {
    toAssign: string;
    arrival: string;
}

/**
 * Une carte par passage : un ménage porte le prénom de qui s'en charge (ou
 * « À assigner »), une arrivée son libellé. Le détail reste dans l'infobulle.
 */
export function planningCardTitle(event: LogementShowPlanningEvent, labels: PlanningCardLabels): string {
    if (event.kind === 'arrival') {
        return labels.arrival;
    }
    const names = event.housekeepers.map((name) => name.trim().split(/\s+/)[0]).filter(Boolean);
    return event.assigned && names.length > 0 ? names.join(', ') : labels.toAssign;
}

export function planningToEvents(events: LogementShowPlanningEvent[], labels: PlanningCardLabels): EventInput[] {
    return events.map((event) => ({
        start: event.date_iso,
        allDay: true,
        display: 'block',
        title: planningCardTitle(event, labels),
        color: PLANNING_COLORS[planningTone(event)],
        // Texte foncé sur l'ambre (blanc illisible), blanc ailleurs — cf. mini-calendar.css.
        classNames: [`mc-tone-${planningTone(event)}`],
        extendedProps: { planning: event, order: PLANNING_ORDER[event.kind] },
    }));
}

const PLANNING_KIND_KEYS: Record<LogementShowPlanningKind, [string, string]> = {
    departure_cleaning: ['tip_departure_cleaning', 'Ménage de départ'],
    mid_stay_cleaning: ['tip_mid_stay_cleaning', 'Ménage en cours de séjour'],
    arrival: ['legend_arrival', 'Arrivée'],
};

/** Infobulle : nature, voyageur, puis pour un ménage qui s'en charge et s'il est vérifié. */
export function planningTooltipLines(event: LogementShowPlanningEvent, t: Translate): string[] {
    const [key, fallback] = PLANNING_KIND_KEYS[event.kind];
    const lines = [`${t(NS, key, fallback)} · ${frenchDate(event.date_iso)}`, event.guest_name];

    if (event.kind !== 'arrival') {
        lines.push(
            event.housekeepers.length > 0
                ? fill(t(NS, 'tip_housekeepers', 'Assigné à : :names'), { names: event.housekeepers.join(', ') })
                : t(NS, 'tip_unassigned', "Personne n'est assigné"),
        );
        if (event.verified) {
            lines.push(t(NS, 'tip_verified', 'Vérifié'));
        }
    }

    return lines.filter(Boolean);
}
