import { readFileSync, readdirSync } from 'node:fs';
import { resolve } from 'node:path';
import { describe, expect, it } from 'vitest';
import { TOURS } from '../../../lib/tour/tours';
import { logementShowTour } from '../../../lib/tour/tours/logement-show.tour';

/**
 * Garde-fous de la visite de la fiche logement (TL-305), lus en TEXTE comme
 * `yield-tour.test.ts` : chaque ancre déclarée est posée une seule fois dans
 * l'écran, et les textes tiennent sur un téléphone sans nommer de fournisseur.
 */

const REPO_ROOT = resolve(__dirname, '../../../../..');
const PAGE_DIR = resolve(REPO_ROOT, 'resources/js/pages/logements/info-logement');

const SOURCES = readdirSync(PAGE_DIR, { recursive: true, encoding: 'utf8' })
    .filter((path) => path.endsWith('.tsx'))
    .map((path) => readFileSync(resolve(PAGE_DIR, path), 'utf8'));

/** Nombre de lignes qui POSENT l'ancre (`data-tour=` ou le prop `dataTour=` d'un composant qui la relaie). */
function assignments(anchor: string): number {
    return SOURCES.flatMap((source) => source.split('\n')).filter(
        (line) => /(data-tour|dataTour)=/.test(line) && (line.includes(`'${anchor}'`) || line.includes(`"${anchor}"`)),
    ).length;
}

function tourLines(locale: 'fr' | 'en'): string[] {
    return readFileSync(resolve(REPO_ROOT, `lang/${locale}/tours.php`), 'utf8')
        .split('\n')
        .filter((line) => /^\s*'logementShow\.[A-Za-z]+\.(title|content)'\s*=>/.test(line));
}

/** Texte VISIBLE d'une ligne `'clé' => '…',` : sans la clé, les guillemets ni les balises. */
function visibleText(line: string): string {
    return line
        .replace(/^\s*'[^']+'\s*=>\s*/, '')
        .replace(/,\s*$/, '')
        .slice(1, -1)
        .replace(/\\'/g, "'")
        .replace(/<[^>]+>/g, '');
}

describe('visite de la fiche logement', () => {
    it('est rattachée à la page Inertia et déclarée au registre', () => {
        expect(logementShowTour.components).toEqual(['logements/info-logement/index']);
        expect(TOURS).toContain(logementShowTour);
    });

    it.each(logementShowTour.steps.map((step) => [step.anchor]))("l'ancre %s est posée exactement une fois dans l'écran", (anchor) => {
        expect(assignments(anchor)).toBe(1);
    });

    it('seuls « Historique » (compte TLL master) et « Calendrier et planning » (droits calendrier / planning) peuvent manquer', () => {
        for (const step of logementShowTour.steps) {
            expect(Boolean(step.optional), step.anchor).toBe(['history', 'instructions', 'calendars'].includes(step.key));
        }
    });

    it('« Calendrier et planning » se place entre le propriétaire et les onglets, comme à l’écran', () => {
        const order = (key: string) => logementShowTour.steps.find((step) => step.key === key)?.order ?? NaN;
        expect(order('calendars')).toBeGreaterThan(order('owner'));
        expect(order('calendars')).toBeLessThan(order('tabs'));
    });

    it('le calendrier du logement est ouvert d’office ; le planning ne se charge qu’à son ouverture', () => {
        const card = readFileSync(resolve(PAGE_DIR, 'components/calendars-card.tsx'), 'utf8');
        const reloads = card.match(/router\.reload\(/g) ?? [];
        // L'unique rechargement vit dans `load` : ouverture d'un volet, « Réessayer », ou le volet ouvert par défaut.
        expect(reloads).toHaveLength(1);
        expect(card).toMatch(/only: \[key\]/);
        expect(card).toMatch(/defaultValue=\{calendarHref \? 'availability' : undefined\}/);
        // Le calendrier ouvert d'office arrive par la prop DIFFÉRÉE du serveur :
        // aucun effet ne lance de rechargement (il partirait avant l'initialisation
        // du routeur Inertia et se perdrait au rechargement complet de la page).
        expect(card).not.toMatch(/useEffect\([^)]*\{[^}]*load\(/s);
        expect(card).not.toMatch(/load\('planning'\)/);
    });

    it('« Modifier » suit le droit de modification des logements', () => {
        expect(logementShowTour.steps.find((step) => step.key === 'edit')?.permission).toBe('property.update');
    });

    it.each(['fr', 'en'] as const)('(%s) contenus de 280 caractères visibles au plus, titres de 45, sans fournisseur', (locale) => {
        const lines = tourLines(locale);
        expect(lines.length).toBe(logementShowTour.steps.length * 2);
        for (const line of lines) {
            const limit = /\.title'/.test(line) ? 45 : 280;
            expect(visibleText(line).length, line.slice(0, 60)).toBeLessThanOrEqual(limit);
            expect(line).not.toMatch(/Rentals\s*United|\bRU\b|Airbnb|Booking\.com/i);
        }
    });
});
