import { describe, expect, it } from 'vitest';
import {
    countSteps,
    droppedCustom,
    groupRows,
    newCustomId,
    pick,
    readSettings,
    sameSettings,
    sharedSettings,
    toggleHidden,
    toggleOpen,
    togglePhoto,
    type CleaningSettings,
    type PlanGroup,
} from './cleaning-checklist';

const chambre: PlanGroup = {
    key: 'chambre-1',
    code: 'chambre',
    kind: 'chambre',
    label: { fr: 'Chambre', ar: 'غرفة النوم', en: 'Bedroom' },
    steps: [
        {
            ref: 'chambre-1:literie',
            step_key: 'chambre.literie',
            label: { fr: 'Literie', en: 'Bedding' },
            details: { fr: 'Faire le lit.' },
            photo: true,
            photo_default: true,
            hidden: false,
            custom: null,
        },
        {
            ref: 'chambre-1:placards',
            step_key: 'chambre.placards',
            label: { fr: 'Placards et cintres vérifiés' },
            details: null,
            photo: false,
            photo_default: false,
            hidden: false,
            custom: null,
        },
    ],
};

const salon: PlanGroup = {
    key: 'salon-1',
    code: 'salon',
    kind: 'salon',
    label: { fr: 'Salon' },
    steps: [
        {
            ref: 'salon-1:surfaces',
            step_key: 'salon.surfaces',
            label: { fr: 'Surfaces et meubles' },
            details: null,
            photo: false,
            photo_default: false,
            hidden: false,
            custom: null,
        },
    ],
};

const empty: CleaningSettings = sharedSettings();

describe('pièces pour le ménage', () => {
    it('lit chaque texte dans la langue du CRM, le français sinon', () => {
        expect(pick({ fr: 'Literie', en: 'Bedding' }, 'en')).toBe('Bedding');
        expect(pick({ fr: 'Arroser les plantes' }, 'en')).toBe('Arroser les plantes');
        expect(pick(null, 'fr')).toBe('');
    });

    it('relit les réglages du serveur, même un `photos` vide servi en tableau', () => {
        expect(readSettings({ hidden: ['a'], photos: [] as unknown as Record<string, boolean>, custom: [] })).toEqual({
            hidden: ['a'],
            photos: {},
            custom: [],
        });
        expect(readSettings(null)).toEqual(empty);
    });

    it('compte les étapes et les photos, une étape masquée en moins', () => {
        expect(countSteps([chambre, salon], empty)).toEqual({ steps: 3, photos: 1 });
        expect(countSteps([chambre, salon], toggleHidden(empty, 'chambre-1:placards'))).toEqual({ steps: 2, photos: 1 });
        // Masquer puis rétablir : on revient au même réglage.
        expect(toggleHidden(toggleHidden(empty, 'chambre-1:placards'), 'chambre-1:placards')).toEqual(empty);
    });

    it("n'enregistre d'une photo que l'écart au catalogue", () => {
        const [literie] = groupRows(chambre, empty);
        const withoutPhoto = togglePhoto(empty, literie, true);
        expect(withoutPhoto.photos).toEqual({ 'chambre-1:literie': false });
        expect(countSteps([chambre, salon], withoutPhoto).photos).toBe(0);

        const [again] = groupRows(chambre, withoutPhoto);
        expect(togglePhoto(withoutPhoto, again, true).photos).toEqual({});
    });

    it('place une étape ajoutée en fin de sa pièce, en français, et règle sa photo à part', () => {
        const settings: CleaningSettings = {
            ...empty,
            custom: [{ id: 'k3f9a2', group: 'salon-1', label: 'Arroser les plantes', details: null, photo: false }],
        };
        const rows = groupRows(salon, settings);
        expect(rows.map((row) => row.ref)).toEqual(['salon-1:surfaces', 'custom:k3f9a2']);
        expect(rows[1].label).toEqual({ fr: 'Arroser les plantes' });

        const withPhoto = togglePhoto(settings, rows[1], false);
        expect(withPhoto.custom[0].photo).toBe(true);
        expect(withPhoto.photos).toEqual({});
        expect(countSteps([chambre, salon], withPhoto)).toEqual({ steps: 4, photos: 2 });
    });

    it("signale les étapes ajoutées dans une pièce que la fiche n'a plus", () => {
        const settings: CleaningSettings = {
            ...empty,
            custom: [
                { id: 'aa11aa11', group: 'chambre-1', label: 'Coffre-fort', details: null, photo: false },
                { id: 'bb22bb22', group: 'chambre-2', label: 'Lit bébé', details: null, photo: false },
            ],
        };
        expect(droppedCustom([chambre, salon], settings).map((step) => step.id)).toEqual(['bb22bb22']);
    });

    it('ouvre et referme le détail de chaque étape séparément', () => {
        const once = toggleOpen(new Set(), 'chambre-1:literie');
        expect([...once]).toEqual(['chambre-1:literie']);
        expect([...toggleOpen(once, 'salon-1:surfaces')].sort()).toEqual(['chambre-1:literie', 'salon-1:surfaces']);
        expect([...toggleOpen(once, 'chambre-1:literie')]).toEqual([]);
    });

    it('fabrique des identifiants que le serveur accepte, sans doublon', () => {
        const id = newCustomId([]);
        expect(id).toMatch(/^[a-z0-9]{8}$/);

        let calls = 0;
        const stuck = () => (calls++ < 8 ? 0 : 0.5);
        expect(newCustomId(['aaaaaaaa'], stuck)).not.toBe('aaaaaaaa');
    });

    it("voit qu'un formulaire n'a pas bougé, quel que soit l'ordre", () => {
        const a: CleaningSettings = { hidden: ['b', 'a'], photos: { x: true }, custom: [] };
        const b: CleaningSettings = { hidden: ['a', 'b'], photos: { x: true }, custom: [] };
        expect(sameSettings(a, b)).toBe(true);
        expect(sameSettings(a, { ...b, photos: {} })).toBe(false);
    });
});
