import { readFileSync } from 'node:fs';
import { resolve } from 'node:path';
import { createElement } from 'react';
import { renderToString } from 'react-dom/server';
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
import type { SeasonTemplate } from '../../logements/season-range-utils';
import type { MovableHolidays, YieldSeason, YieldSeasonality } from '../types';
import SeasonsEditor from './rules/SeasonsEditor';
import YieldSeasonRow from './YieldSeasonRow';

/**
 * L'onglet Saisons tel qu'il sort du rendu SERVEUR (`react-dom/server`,
 * environnement `node`). season-calendar.test.ts prouve la logique ; ce test
 * prouve que les composants l'APPLIQUENT — l'écran ne doit pas mentir sur ce
 * que fait le moteur :
 *  - badges « Chaque année », « Événement prioritaire », « Date mobile » ;
 *  - dates d'un Aïd en LECTURE SEULE (aucun champ date), prochaine
 *    occurrence d'après la table, avertissement ambre sans date à venir ;
 *  - galerie qui grise la puce « Haute sept » d'une configuration reprise,
 *    et les puces « Aïd » dès que la fête est déjà là (sinon : clic = l'Aïd
 *    en double, en saison à dates fixes) ;
 *  - frise qui montre l'Aïd el-Fitr dans « Basse hiver ».
 */

vi.mock('@inertiajs/react', () => ({
    usePage: () => ({ props: { auth: { permissions: ['yield.view', 'yield.manage'] }, translations: {}, locale: 'fr' } }),
}));

const FIXTURES = resolve(__dirname, '../../../../../tests/Fixtures/yield');
const population = (letter: 'a' | 'b') =>
    JSON.parse(readFileSync(resolve(FIXTURES, `prod-seasonality-population-${letter}.json`), 'utf8')) as { converted: YieldSeasonality };
const POPULATION_A = population('a');
const POPULATION_B = population('b');

const HOLIDAYS: MovableHolidays = {
    eid_al_fitr: { dates: ['2027-03-10', '2028-02-26'], confirmed: [] },
    eid_al_adha: { dates: ['2027-05-17'], confirmed: [] },
};

const FITR: YieldSeason = {
    name: 'Aïd Fitr',
    start_date: '2027-03-08',
    end_date: '2027-03-11',
    pct: 30,
    layer: 'event',
    holiday: 'eid_al_fitr',
    offset_start: -2,
    offset_end: 1,
};

const noop = () => undefined;
const row = (value: YieldSeason, today = '2026-09-25', holidays: MovableHolidays = HOLIDAYS) =>
    renderToString(createElement(YieldSeasonRow, { value, today, holidays, onChange: noop, onRemove: noop }));

/** Texte visible d'un rendu (balises retirées, entités décodées). */
const text = (html: string): string =>
    html
        .replace(/<[^>]+>/g, ' ')
        .replace(/&#x27;/g, "'")
        .replace(/&amp;/g, '&')
        .replace(/&gt;/g, '>')
        .replace(/\s+/g, ' ');

describe('YieldSeasonRow (rendu)', () => {
    it('saison ponctuelle : deux champs date, aucun badge', () => {
        const html = row({ name: 'Promo', start_date: '2026-10-01', end_date: '2026-10-10', pct: -10 });

        expect((html.match(/type="date"/g) ?? []).length).toBe(2);
        expect(text(html)).not.toMatch(/Chaque année|Événement prioritaire|Date mobile|Prochaine occurrence/);
    });

    it('saison annuelle : badge « Chaque année », dates modifiables, prochaine occurrence', () => {
        const html = row({ name: 'Haute sept', start_date: '2026-09-01', end_date: '2026-09-15', pct: 20, repeat: 'yearly' });

        expect(text(html)).toContain('Chaque année');
        expect((html.match(/type="date"/g) ?? []).length).toBe(2);
        expect(text(html)).toContain('Prochaine occurrence : 01/09/2027 → 15/09/2027');
        expect(text(html)).not.toContain('Événement prioritaire');
    });

    it('Aïd : badges, dates en LECTURE SEULE d’après la table, estimée à un jour près', () => {
        const html = row(FITR);

        expect(text(html)).toContain('Événement prioritaire');
        expect(text(html)).toContain('Date mobile : Aïd el-Fitr (J-2 → J+1)');
        expect(html).not.toMatch(/type="date"/);
        expect(text(html)).toContain('Prochaine occurrence : 08/03/2027 → 11/03/2027');
        expect(text(html)).toContain('date estimée à un jour près');
        // Le nom et le % restent modifiables.
        expect(html).toMatch(/type="text"[^>]*value="Aïd Fitr"/);
        expect(html).toMatch(/type="number"[^>]*value="30"/);
        expect(text(html)).not.toContain('Aucune date connue');
    });

    it('Aïd sans date dans les 12 mois : avertissement ambre, aucune occurrence inventée', () => {
        const html = row(FITR, '2028-06-01');

        expect(text(html)).toContain('Aucune date connue pour cette fête dans les 12 prochains mois : cette ligne ne modifie aucun prix.');
        expect(text(html)).not.toContain('Prochaine occurrence');
    });

    it('date confirmée : plus de mention « estimée »', () => {
        const html = row(FITR, '2026-09-25', { eid_al_fitr: { dates: ['2027-03-10'], confirmed: ['2027-03-10'] } });

        expect(text(html)).toContain('Prochaine occurrence : 08/03/2027 → 11/03/2027');
        expect(text(html)).not.toContain('estimée');
    });
});

describe('SeasonsEditor (rendu)', () => {
    const TEMPLATES: SeasonTemplate[] = [
        { name: 'Haute sept', start: '09-01', end: '09-15', intensity: 'high' },
        { name: 'Basse hiver', start: '01-07', end: '03-19', intensity: 'low' },
        { name: 'Aïd Fitr', start: '03-05', end: '03-10', intensity: 'event', holiday: 'eid_al_fitr', offsets: [-2, 1] },
        { name: 'Aïd Adha', start: '05-14', end: '05-17', intensity: 'event', holiday: 'eid_al_adha', offsets: [-2, 1] },
        { name: 'Printemps', start: '05-20', end: '05-31', intensity: 'mid' },
    ];

    beforeEach(() => {
        vi.useFakeTimers();
        vi.setSystemTime(new Date(2026, 8, 25, 12));
    });
    afterEach(() => {
        vi.useRealTimers();
    });

    /** Boutons de la galerie : nom de la puce → grisée ? */
    const chips = (html: string): Record<string, boolean> => {
        const result: Record<string, boolean> = {};
        for (const match of html.matchAll(/<button([^>]*)>(.*?)<\/button>/gs)) {
            const name = TEMPLATES.find((tpl) => text(match[2]).includes(tpl.name))?.name;
            if (name) result[name] = /\sdisabled=""/.test(match[1]);
        }
        return result;
    };

    it.each([
        ['A', POPULATION_A],
        ['B', POPULATION_B],
    ] as const)('galerie, population %s reprise : « Haute sept » et les deux Aïd grisés, pas une puce libre', (_label, pop) => {
        const html = renderToString(
            createElement(SeasonsEditor, { value: pop.converted, onChange: noop, seasonTemplates: TEMPLATES, holidays: HOLIDAYS }),
        );

        expect(chips(html)).toEqual({ 'Haute sept': true, 'Basse hiver': true, 'Aïd Fitr': true, 'Aïd Adha': true, Printemps: false });
    });

    it('galerie : sans Aïd, les puces « Aïd » restent libres dans « Basse hiver » et montrent la date mobile', () => {
        const basseHiver: YieldSeason = { name: 'Basse hiver', start_date: '2027-01-07', end_date: '2027-03-19', pct: -15, repeat: 'yearly' };
        const html = renderToString(
            createElement(SeasonsEditor, {
                value: { base_pct: 0, seasons: [basseHiver] },
                onChange: noop,
                seasonTemplates: TEMPLATES,
                holidays: HOLIDAYS,
            }),
        );

        expect(chips(html)).toMatchObject({ 'Basse hiver': true, 'Aïd Fitr': false, 'Aïd Adha': false });
        // La fenêtre J-2 → J+1 d'après la table, pas les MM-JJ fixes du gabarit (05/03 → 10/03, 14/05 → 17/05).
        expect(text(html)).toContain('Aïd Fitr 08/03/27 → 11/03/27');
        expect(text(html)).toContain('Aïd Adha 15/05/27 → 18/05/27');
    });

    it('frise : l’Aïd el-Fitr apparaît, l’emportant sur « Basse hiver »', () => {
        const html = renderToString(
            createElement(SeasonsEditor, { value: POPULATION_A.converted, onChange: noop, seasonTemplates: TEMPLATES, holidays: HOLIDAYS }),
        );

        expect(html).toContain('title="Aïd Fitr +30% · 08/03/27 → 11/03/27"');
        expect(html).toContain('title="Basse hiver -15% · 07/01/27 → 07/03/27"');
        expect(html).toContain('title="Basse hiver -15% · 12/03/27 → 19/03/27"');
    });

    it('sans la table des fêtes, l’Aïd ne tombe sur aucune nuit — et la ligne le dit', () => {
        const html = renderToString(
            createElement(SeasonsEditor, { value: { base_pct: 0, seasons: [FITR] }, onChange: noop, seasonTemplates: TEMPLATES }),
        );

        expect(html).not.toContain('title="Aïd Fitr');
        expect(text(html)).toContain('Aucune date connue pour cette fête');
    });
});
