// ============================================================================
// Contrat de baseline QA · tokens de marque + helpers partagés
// ----------------------------------------------------------------------------
// Ces constantes matérialisent les NON-NÉGOCIABLES de CLAUDE.md (#4 design
// luxury) sous forme d'assertions vérifiables. AUCUN chiffre projet (prix,
// superficie, disponibilité) n'est asserté ici — contrainte #6 (zéro invention).
// ============================================================================
import type { Page, Response } from '@playwright/test';

/** Design luxury dark+doré — CLAUDE.md contrainte #4. */
export const LUXURY = {
  /** Couleurs de marque (hex, insensible à la casse). */
  colors: ['#0a0a12', '#f0b429'],
  /** Familles de police officielles. */
  fonts: ['Fraunces', 'Cormorant Garamond', 'Cormorant'],
} as const;

export interface RouteSpec {
  path: string;
  name: string;
  gated: boolean;
  note?: string;
}

export interface RoutesManifest {
  comment?: string;
  routes: RouteSpec[];
}

/** Une page est « accessible » si le serveur ne renvoie pas d'erreur (< 400).
 *  Une redirection login (3xx→200 sur /login) reste accessible. */
export function isReachable(res: Response | null): boolean {
  if (!res) return false;
  return res.status() < 400;
}

/** Détecte un mur de login (route gated non authentifiée) sans échouer le test. */
export function looksLikeLoginGate(url: string, bodyText: string): boolean {
  const u = url.toLowerCase();
  if (u.includes('/login') || u.includes('/app/login')) return true;
  const b = bodyText.toLowerCase();
  return /\b(login|se connecter|sign in|mot de passe|password)\b/.test(b)
    && b.length < 4000; // heuristique : page de login = courte
}

/** Récupère le HTML rendu + le texte de toutes les feuilles de style liées,
 *  pour chercher les tokens de marque dans le CSS réel servi. */
export async function collectStyleSurface(page: Page): Promise<string> {
  const html = await page.content();
  const cssHrefs = await page.$$eval(
    'link[rel="stylesheet"]',
    (links) => links.map((l) => (l as HTMLLinkElement).href).filter(Boolean),
  );
  const inlineCss = await page.$$eval('style', (els) =>
    els.map((e) => e.textContent || '').join('\n'),
  );

  const parts = [html, inlineCss];
  // Feuilles de style externes : on tente de les lire via le contexte réseau
  // de la page (même origine / mêmes cookies). Best-effort, tolérant aux échecs.
  for (const href of cssHrefs.slice(0, 12)) {
    try {
      const r = await page.request.get(href);
      if (r.ok()) parts.push(await r.text());
    } catch {
      /* réseau/CORS — ignoré, best-effort */
    }
  }
  return parts.join('\n');
}

/** Vrai si `haystack` contient au moins un des `needles` (insensible à la casse). */
export function containsAny(haystack: string, needles: readonly string[]): boolean {
  const h = haystack.toLowerCase();
  return needles.some((n) => h.includes(n.toLowerCase()));
}
