import { createElement } from 'react';
import { renderToString } from 'react-dom/server';
import { describe, expect, it, vi } from 'vitest';
import type { RuleSectionValues } from '../../types';
import YieldRuleEditor, { type YieldConfigData } from '../YieldRuleEditor';
import WeekendEditor from './WeekendEditor';

/**
 * La règle week-end telle qu'elle sort du rendu SERVEUR (`react-dom/server`,
 * environnement `node` : le projet n'a ni jsdom ni @testing-library).
 *
 * `rule-sections.test.ts` prouve la logique (normalisation, interrupteur,
 * cases) ; ce test prouve que les composants l'APPLIQUENT : les cases cochées
 * sont celles de la règle, l'équivalent « soit +10 % » suit le multiplicateur,
 * l'aide dit ce que fait ×1, et l'éditeur porte un cinquième onglet ancré pour
 * la visite de yield/edit.
 */

/**
 * Le champ de saisie, rendu à l'identique mais ses props RELEVÉES : le rendu
 * serveur ne déclenche aucun événement, on appelle donc son onChange à la main
 * (un setState appelé hors rendu est sans effet côté serveur).
 */
const { inputs, checkboxes } = vi.hoisted(() => ({
    inputs: [] as Array<{ value?: unknown; onChange?: (e: { target: { value: string } }) => void }>,
    checkboxes: [] as Array<{ checked?: boolean; onCheckedChange?: (checked: boolean) => void }>,
}));
vi.mock('@/components/ui/input', async () => {
    const { createElement } = await import('react');
    return {
        Input: (props: Record<string, unknown>) => {
            inputs.push(props);
            return createElement('input', props);
        },
    };
});
// Même balisage que la case Radix pour ce que les tests comptent (role, aria-checked).
vi.mock('@/components/ui/checkbox', async () => {
    const { createElement } = await import('react');
    return {
        Checkbox: (props: { checked?: boolean; onCheckedChange?: (checked: boolean) => void }) => {
            checkboxes.push(props);
            return createElement('button', { type: 'button', role: 'checkbox', 'aria-checked': String(props.checked === true) });
        },
    };
});

vi.mock('@inertiajs/react', () => ({
    usePage: () => ({ props: { auth: { permissions: ['yield.view', 'yield.manage'] }, translations: {}, locale: 'fr' } }),
    // useForm réduit à ce que l'éditeur lit au rendu : les valeurs initiales.
    useForm: <T>(initial: T) => ({ data: initial, setData: vi.fn(), put: vi.fn(), processing: false }),
    router: { post: vi.fn(), put: vi.fn(), visit: vi.fn() },
}));

const noop = () => undefined;

/** Nombre de cases Radix cochées dans un rendu. */
const checkedBoxes = (html: string): number => (html.match(/role="checkbox"[^>]*aria-checked="true"/g) ?? []).length;

describe('WeekendEditor (rendu)', () => {
    it('coche exactement les nuits de la règle, parmi sept', () => {
        const html = renderToString(createElement(WeekendEditor, { value: { multiplier: 1.1, days: [5, 6] }, onChange: noop }));

        expect((html.match(/role="checkbox"/g) ?? []).length).toBe(7);
        expect(checkedBoxes(html)).toBe(2);
        expect(html).toContain('Nuit du vendredi');
        expect(html).toContain('Nuit du lundi');
    });

    it("affiche l'équivalent en % du multiplicateur, et l'aide « ×1 = aucun effet »", () => {
        const up = renderToString(createElement(WeekendEditor, { value: { multiplier: 1.1, days: [5, 6] }, onChange: noop }));
        expect(up).toContain('soit +10 %');
        expect(up).toContain('×1 = aucun effet');
        expect(up).toContain('bande Min/Max');

        const down = renderToString(createElement(WeekendEditor, { value: { multiplier: 0.9, days: [5, 6] }, onChange: noop }));
        expect(down).toContain('soit −10 %');
    });

    it('prévient quand aucune nuit n’est cochée', () => {
        const none = renderToString(createElement(WeekendEditor, { value: { multiplier: 1.2, days: [] }, onChange: noop }));
        expect(checkedBoxes(none)).toBe(0);
        expect(none).toContain('Cochez au moins une nuit');
        // Le texte dit ce que fait le serveur : une règle sans nuit est REFUSÉE (422).
        expect(none).toContain('ne peut pas être enregistrée');

        const some = renderToString(createElement(WeekendEditor, { value: { multiplier: 1.2, days: [7] }, onChange: noop }));
        expect(some).not.toContain('Cochez au moins une nuit');
    });

    it('affiche le multiplicateur TEL QUEL : 0 reste « 0 » (et non « 1 »), un champ vidé reste vide', () => {
        const zero = renderToString(createElement(WeekendEditor, { value: { multiplier: 0, days: [5, 6] }, onChange: noop }));
        expect(zero).toMatch(/<input[^>]*type="number"[^>]*value="0"/);
        expect(zero).toContain('soit −100\u00a0%');

        const cleared = renderToString(createElement(WeekendEditor, { value: { multiplier: Number.NaN, days: [5, 6] }, onChange: noop }));
        expect(cleared).toMatch(/<input[^>]*type="number"[^>]*value=""/);
        expect(cleared).toContain('soit —');
    });

    it('la saisie remonte le nombre TAPÉ, jours conservés — « 0.9 » donne ×0,9, un champ vidé NaN', () => {
        const changes: unknown[] = [];
        inputs.length = 0;
        renderToString(createElement(WeekendEditor, { value: { multiplier: 1.1, days: [5, 6] }, onChange: (rule: unknown) => changes.push(rule) }));
        const field = inputs.at(-1);
        expect(field?.value).toBe('1.1');

        field?.onChange?.({ target: { value: '0' } });
        field?.onChange?.({ target: { value: '0.9' } });
        field?.onChange?.({ target: { value: '' } });

        expect(changes[0]).toEqual({ multiplier: 0, days: [5, 6] });
        expect(changes[1]).toEqual({ multiplier: 0.9, days: [5, 6] });
        expect((changes[2] as { multiplier: number }).multiplier).toBeNaN();
    });

    it('cocher une nuit alors que le champ vaut 0 garde 0 (le serveur le refusera), sans le changer en ×1', () => {
        const changes: unknown[] = [];
        checkboxes.length = 0;
        renderToString(createElement(WeekendEditor, { value: { multiplier: 0, days: [5] }, onChange: (rule: unknown) => changes.push(rule) }));

        expect(checkboxes).toHaveLength(7);
        checkboxes[5].onCheckedChange?.(true); // nuit du samedi
        checkboxes[4].onCheckedChange?.(false); // nuit du vendredi

        expect(changes).toEqual([
            { multiplier: 0, days: [5, 6] },
            { multiplier: 0, days: [] },
        ]);
    });

    it('les bornes du champ sont celles du serveur (×0,5 à ×2)', () => {
        const html = renderToString(createElement(WeekendEditor, { value: { multiplier: 1, days: [5, 6] }, onChange: noop }));
        expect(html).toMatch(/type="number"[^>]*min="0.5"[^>]*max="2"/);
    });
});

/** Mêmes formes que YieldConfig::sectionDefaults() / sectionDisabledValues(), réduites. */
const DEFAULTS: RuleSectionValues = {
    seasonality: { base_pct: 0, seasons: [] },
    last_minute_rules: [],
    far_out_premium: { threshold_days: 60, premium_pct: 0.1 },
    oba_rules: [],
    weekend_rule: { multiplier: 1.1, days: [5, 6] },
};
const DISABLED: RuleSectionValues = {
    seasonality: { base_pct: 0, seasons: [] },
    last_minute_rules: [],
    far_out_premium: { threshold_days: 60, premium_pct: 0 },
    oba_rules: [],
    weekend_rule: { multiplier: 1, days: [5, 6] },
};

function config(overrides: Partial<YieldConfigData> = {}): YieldConfigData {
    return {
        id: 1,
        property_id: '100',
        internal_id: 100,
        property_name: 'Villa',
        is_active: true,
        min_price: 50,
        base_price: 80,
        max_price: 150,
        seasonality: { base_pct: 0, seasons: [] },
        last_minute_rules: [],
        oba_rules: [],
        far_out_premium: { threshold_days: 60, premium_pct: 0 },
        weekend_rule: { multiplier: 1, days: [5, 6] },
        rule_group_id: null,
        rule_overrides: [],
        rule_group: null,
        last_pushed_at: null,
        last_push_status: null,
        ...overrides,
    };
}

function renderEditor(overrides: Partial<YieldConfigData> = {}): string {
    return renderToString(
        createElement(YieldRuleEditor, {
            config: config(overrides),
            obaPresets: {},
            exchangeRate: 2.9,
            sectionDefaults: DEFAULTS,
            sectionDisabled: DISABLED,
        }),
    );
}

describe('YieldRuleEditor — onglet Week-end (rendu)', () => {
    it('porte un cinquième onglet « Week-end », ancré pour la visite sur son déclencheur', () => {
        const html = renderEditor();

        expect((html.match(/role="tab"/g) ?? []).length).toBe(5);
        expect(html).toMatch(/<button[^>]*role="tab"[^>]*data-tour="yield-edit-weekend"[^>]*>(?:(?!<\/button>).)*Week-end/);
    });

    it('rend sans erreur quand le serveur (ancien) ne sert pas de weekend_rule', () => {
        const legacy = config();
        delete (legacy as Partial<YieldConfigData>).weekend_rule;

        expect(() =>
            renderToString(
                createElement(YieldRuleEditor, {
                    config: legacy,
                    obaPresets: {},
                    exchangeRate: 2.9,
                    sectionDefaults: DEFAULTS,
                    sectionDisabled: DISABLED,
                }),
            ),
        ).not.toThrow();
    });
});
