// Auteur : Simon-Pierre Boucher — contact@spboucher.ai
"use client";
import Link from "next/link";
import { useLang } from "@/components/LangContext";

type Row = [string, string];
interface Section {
  num: string;
  title: string;
  paras: string[];
  table?: { head: [string, string, string]; rows: [string, string, string][] };
  kv?: Row[];
}

const FR: Section[] = [
  {
    num: "01",
    title: "Données sources",
    paras: [
      "Deux corpus publics forment la fondation de Vrai-Prix. Premièrement, les rôles d'évaluation foncière géoréférencés du Québec, publiés en données ouvertes par le ministère des Affaires municipales et de l'Habitation (MAMH) : six millésimes complets (2021 à 2026), de 3 626 120 à 3 747 008 unités d'évaluation par millésime, soit 22,15 millions d'observations unité-année. Chaque unité porte sa géométrie ponctuelle (NAD83, EPSG:4269), son matricule, son usage (code CUBF), ses caractéristiques de bâtiment et de terrain, et ses valeurs au rôle.",
      "Deuxièmement, 745 119 transactions immobilières publiées entre janvier 2021 et juillet 2026, couvrant 1 100 municipalités, chacune géolocalisée et documentée (prix, date, type, année de construction, aire d'étages, valeurs au rôle).",
    ],
    kv: [
      ["Millésimes de rôle", "2021 · 2022 · 2023 · 2024 · 2025 · 2026"],
      ["Unités d'évaluation (2026)", "3 747 008"],
      ["Observations unité-année", "22 150 285"],
      ["Transactions", "745 119 (2021-01 → 2026-07)"],
      ["Municipalités couvertes", "1 100"],
      ["Système de référence", "NAD83 (EPSG:4269), projeté EPSG:32198"],
    ],
  },
  {
    num: "02",
    title: "Fusion spatiale transaction ↔ unité d'évaluation",
    paras: [
      "Chaque transaction est appariée à son unité d'évaluation par coordonnées XY, dans le rôle de l'année de la transaction. L'appariement se fait en deux passes, en coordonnées projetées Québec Lambert (EPSG:32198).",
      "Passe A — spatiale : parmi les 25 plus proches voisins dans un rayon de 200 m, chaque candidat reçoit un score : +100 si la valeur au rôle est identique à celle portée par la transaction, +60 si la valeur du rôle antérieur concorde, +30 si l'année de construction concorde, +30 si l'aire d'étages concorde à ±0,6 m², moins 0,5 point par mètre de distance. Le meilleur score gagne.",
      "Passe B — jointure par valeur : les tours à condos placent des centaines d'unités sur le même point ; la géométrie seule ne suffit pas. Pour toute transaction sans concordance exacte de valeur en passe A, on cherche l'unité de valeur au rôle strictement identique dans un rayon de 500 m. En dernier recours, les millésimes adjacents (année ±1) sont essayés — les rôles triennaux étant déposés à des années différentes selon les municipalités.",
      "Résultat : 99,88 % des transactions appariées, 82,2 % avec concordance exacte de la valeur au rôle, distance médiane de 0,6 m (p90 = 12 m). Ce lien est vérifiable transaction par transaction.",
    ],
  },
  {
    num: "03",
    title: "Modèle hédonique de masse",
    paras: [
      "Le cœur du système est un modèle de gradient boosting (LightGBM) entraîné à prédire le logarithme du prix de vente. L'échantillon d'entraînement compte 689 231 transactions, après exclusion des ventes non représentatives du marché : appariement incertain, prix hors de 50 000 $ à 20 M$, ratio prix/évaluation hors de l'intervalle [0,3 ; 4].",
      "Dix-huit variables décrivent chaque propriété au moment de la vente : aire d'étages, superficie et frontage du terrain, nombre d'étages, de logements, de locaux non résidentiels et de chambres locatives, âge du bâtiment, usage CUBF (et sa famille), lien physique, genre de construction, municipalité et région (variables catégorielles), latitude, longitude, temps continu (mois écoulés) et mois de l'année, plus les valeurs au rôle (terrain, bâtiment, total, croissance vs rôle antérieur, part du terrain).",
      "Hyperparamètres : 255 feuilles, taux d'apprentissage 0,05, sous-échantillonnage 85 %, arrêt précoce sur ensemble de validation (~825 itérations retenues). La rétro-transformation du logarithme applique le facteur de correction de Duan (smearing, 1,04). Deux modèles de régression quantile (α = 0,10 et 0,90), entraînés sur les mêmes variables, produisent la fourchette P10-P90 propre à chaque propriété. Un second modèle « hédonique pur », sans les valeurs au rôle, sert de contrôle.",
      "Le modèle final est appliqué aux 3,75 millions d'unités de chaque millésime (temps fixé à la mi-année), produisant 22 millions d'estimations historisées 2021-2026.",
    ],
  },
  {
    num: "04",
    title: "Validation — norme IAAO",
    paras: [
      "La validation suit la norme de l'International Association of Assessing Officers (IAAO) sur les études de ratios : ratio médian (estimation/prix), coefficient de dispersion (COD), différentiel lié au prix (PRD) et coefficient de biais lié au prix (PRB). Deux protocoles : un ensemble aléatoire réservé de 15 % (102 943 ventes jamais vues) et un test temporel strict (entraînement sans 2026, test sur les 68 364 ventes de 2026).",
      "Le COD provincial (22,7) dépasse la cible urbaine IAAO (≤ 15) — attendu pour un modèle unique couvrant aussi les marchés ruraux minces ; le segment condo (11,9) y satisfait pleinement. Le ratio médian de 0,995 indique l'absence de biais systématique.",
    ],
    table: {
      head: ["Métrique", "Holdout aléatoire", "Test temporel 2026"],
      rows: [
        ["Erreur médiane (MdAPE)", "11,0 %", "14,0 %"],
        ["± 20 % du prix réel", "72,1 %", "66,7 %"],
        ["R² (log)", "0,789", "0,743"],
        ["Ratio médian (cible 0,90-1,10)", "0,995", "0,961"],
        ["COD (cible ≤ 15-20)", "22,7 (condos 11,9)", "23,7"],
        ["PRD (cible 0,98-1,03)", "1,092", "1,075"],
        ["PRB (cible ±0,05)", "−0,104", "−0,082"],
      ],
    },
  },
  {
    num: "05",
    title: "Moteur d'estimation en ligne",
    paras: [
      "Au moment de la consultation, l'estimation affichée combine deux sources indépendantes. (1) L'estimation du modèle hédonique, pré-calculée pour la propriété. (2) Une estimation par comparables : les ventes candidates sont cherchées dans un rayon adaptatif (2,2 km, élargi jusqu'à ~28 km si le marché est mince, minimum 30 candidates sur 30-36 mois), filtrées par famille de type et par aire d'étages (±20 %, relâché à ±40 % puis abandonné sous 6 candidates).",
      "Chaque comparable est ajusté, en dollars affichés : ajustement de marché (indice mensuel, section 06), ajustement de superficie (50 % du $/m² du comparable par m² d'écart, plafonné à ±25 % du prix), ajustement d'âge (0,5 % du prix par année d'écart, plafonné à ±10 %). Le poids de chaque comparable est le produit de trois gaussiennes : distance (σ = 1 500 m), récence (σ = 24 mois) et similarité de superficie (σ = 25 %). L'estimation comparables est la médiane pondérée des prix ajustés (12 meilleures retenues).",
      "Combinaison : 65 % modèle + 35 % comparables lorsque les deux existent (≥ 3 comparables) ; sinon la source disponible seule. La fourchette affichée est l'intervalle P10-P90 du modèle, recentré sur l'estimation finale.",
      "L'indice de confiance (0-100, niveaux A ≥ 75, B ≥ 60, C ≥ 45, D < 45) agrège : le nombre de comparables (+2,5/comparable, max 25), leur dispersion (jusqu'à +20 quand la déviation médiane est faible), la présence du modèle (+15) et une pénalité de largeur de fourchette au-delà de 30 %. Aucune estimation n'est affichée sans son niveau de confiance.",
    ],
  },
  {
    num: "06",
    title: "Indice de marché mensuel",
    paras: [
      "L'ajustement temporel des comparables repose sur un indice mensuel par type de propriété : médiane du prix au mètre carré des ventes du mois (minimum 5 ventes), lissée par médiane mobile centrée de 3 mois, normalisée au dernier mois observé. Une vente de janvier 2024 comparée en août 2026 est ainsi ramenée aux conditions de marché actuelles avant tout autre ajustement.",
    ],
  },
  {
    num: "07",
    title: "Limites connues",
    paras: [
      "L'honnêteté méthodologique exige de nommer ce que le système ne voit pas. Les rénovations intérieures, garages, piscines et l'état d'entretien n'apparaissent pas dans les données ouvertes du rôle. Dans les marchés ruraux minces, la dispersion dépasse les cibles IAAO urbaines — l'indice de confiance le reflète. Les unités d'une même tour à condos partagent un point géographique : sans numéro d'appartement, la recherche peut retourner une unité voisine. Les valeurs au rôle utilisées comme variables sont établies 18 à 24 mois avant leur entrée en vigueur. Enfin, une estimation statistique, aussi rigoureuse soit-elle, ne remplace pas l'évaluation d'un évaluateur agréé (OEAQ) ni l'analyse d'un courtier pour une mise en vente.",
    ],
  },
];

const EN: Section[] = [
  {
    num: "01",
    title: "Source data",
    paras: [
      "Two public corpora form the foundation of Vrai-Prix. First, Québec's georeferenced property assessment rolls, published as open data by the MAMH: six complete vintages (2021-2026), 3.63M to 3.75M assessment units each — 22.15 million unit-year observations, each with point geometry (NAD83), roll number, CUBF use code, building and lot characteristics, and assessed values.",
      "Second, 745,119 real-estate transactions published between January 2021 and July 2026 across 1,100 municipalities, each geolocated and documented (price, date, type, year built, floor area, assessed values).",
    ],
    kv: [
      ["Roll vintages", "2021 · 2022 · 2023 · 2024 · 2025 · 2026"],
      ["Assessment units (2026)", "3,747,008"],
      ["Unit-year observations", "22,150,285"],
      ["Transactions", "745,119 (2021-01 → 2026-07)"],
      ["Municipalities", "1,100"],
      ["Reference system", "NAD83 (EPSG:4269), projected EPSG:32198"],
    ],
  },
  {
    num: "02",
    title: "Spatial merge: transaction ↔ assessment unit",
    paras: [
      "Each transaction is matched to its assessment unit by XY coordinates in the roll of its own year, in two passes using Québec Lambert (EPSG:32198) coordinates.",
      "Pass A — spatial: among the 25 nearest neighbours within 200 m, each candidate is scored: +100 for an identical assessed value, +60 for a matching previous-roll value, +30 for a matching year built, +30 for a floor area within ±0.6 m², minus 0.5 point per metre of distance.",
      "Pass B — value join: condo towers stack hundreds of units on one point, so geometry alone is insufficient. For transactions without an exact value match in pass A, we search for the unit with a strictly identical assessed value within 500 m. As a last resort, adjacent vintages (±1 year) are tried, since triennial rolls are deposited in different years across municipalities.",
      "Result: 99.88% of transactions matched, 82.2% with an exact assessed-value match, median distance 0.6 m (p90 = 12 m). The link is verifiable transaction by transaction.",
    ],
  },
  {
    num: "03",
    title: "Mass-appraisal hedonic model",
    paras: [
      "The core is a gradient-boosting model (LightGBM) trained to predict the log of sale price on 689,231 transactions, after excluding non-market sales (uncertain match, price outside $50k-$20M, price/assessment ratio outside [0.3, 4]).",
      "Eighteen variables describe each property at sale time: floor area, lot area and frontage, storeys, dwellings, non-residential units, rental rooms, building age, CUBF use (and family), physical link, construction style, municipality and region (categorical), latitude, longitude, continuous time and month of year, plus assessed values (land, building, total, growth vs previous roll, land share).",
      "Hyperparameters: 255 leaves, learning rate 0.05, 85% subsampling, early stopping (~825 iterations). The log back-transform applies Duan's smearing factor (1.04). Two quantile models (α = 0.10, 0.90) produce each property's P10-P90 range; a value-free 'pure hedonic' variant serves as control.",
      "The final model is applied to all 3.75M units of every vintage (time set to mid-year), producing 22 million historical estimates 2021-2026.",
    ],
  },
  {
    num: "04",
    title: "Validation — IAAO standard",
    paras: [
      "Validation follows the IAAO Standard on Ratio Studies: median ratio, coefficient of dispersion (COD), price-related differential (PRD) and price-related bias (PRB). Two protocols: a 15% random holdout (102,943 unseen sales) and a strict temporal test (trained without 2026, tested on 68,364 sales of 2026).",
      "The province-wide COD (22.7) exceeds the urban IAAO target (≤ 15) — expected for a single model that also covers thin rural markets; the condo segment (11.9) fully meets it. The 0.995 median ratio shows no systematic bias.",
    ],
    table: {
      head: ["Metric", "Random holdout", "Temporal test 2026"],
      rows: [
        ["Median error (MdAPE)", "11.0%", "14.0%"],
        ["Within ±20% of price", "72.1%", "66.7%"],
        ["R² (log)", "0.789", "0.743"],
        ["Median ratio (target 0.90-1.10)", "0.995", "0.961"],
        ["COD (target ≤ 15-20)", "22.7 (condos 11.9)", "23.7"],
        ["PRD (target 0.98-1.03)", "1.092", "1.075"],
        ["PRB (target ±0.05)", "−0.104", "−0.082"],
      ],
    },
  },
  {
    num: "05",
    title: "Online estimation engine",
    paras: [
      "At consultation time, the displayed estimate blends two independent sources. (1) The precomputed hedonic estimate. (2) A comparables estimate: candidate sales are fetched in an adaptive radius (2.2 km, widened up to ~28 km in thin markets, minimum 30 candidates over 30-36 months), filtered by type family and floor area (±20%, relaxed to ±40%, dropped below 6 candidates).",
      "Each comparable is adjusted, in displayed dollars: market adjustment (monthly index, section 06), area adjustment (50% of the comp's $/m² per m² of difference, capped at ±25%), age adjustment (0.5%/year, capped at ±10%). Weights are the product of three Gaussians: distance (σ = 1,500 m), recency (σ = 24 months) and area similarity (σ = 25%). The comparables estimate is the weighted median of adjusted prices (best 12 kept).",
      "Blend: 65% model + 35% comparables when both exist (≥ 3 comps); otherwise the available source alone. The displayed range is the model's P10-P90 interval re-centred on the final estimate.",
      "The confidence index (0-100; A ≥ 75, B ≥ 60, C ≥ 45, D < 45) aggregates comp count (+2.5 each, max 25), comp dispersion (up to +20), model availability (+15) and a penalty for range width beyond 30%. No estimate is ever shown without its confidence level.",
    ],
  },
  {
    num: "06",
    title: "Monthly market index",
    paras: [
      "Time adjustment relies on a monthly index per property type: the median price per square metre of that month's sales (minimum 5), smoothed with a centred 3-month rolling median, normalized to the latest observed month.",
    ],
  },
  {
    num: "07",
    title: "Known limitations",
    paras: [
      "Methodological honesty requires naming what the system cannot see. Interior renovations, garages, pools and upkeep do not appear in open roll data. In thin rural markets, dispersion exceeds urban IAAO targets — the confidence index reflects it. Units in the same condo tower share one geographic point: without an apartment number, search may return a neighbouring unit. Assessed values used as features are set 18-24 months before taking effect. Finally, a statistical estimate, however rigorous, does not replace a chartered appraiser's evaluation (OEAQ) or a broker's analysis for a listing.",
    ],
  },
];

export default function MethodologiePage() {
  const { lang } = useLang();
  const fr = lang === "fr";
  const sections = fr ? FR : EN;
  return (
    <div className="py-10">
      <nav className="vp-mono mb-8 flex items-center gap-2 text-[11.5px] uppercase tracking-[0.06em] text-ink-3">
        <Link href="/" className="border-b-[1.5px] border-transparent hover:border-green hover:text-green">
          Vrai-Prix
        </Link>
        <span>/</span>
        <span>{fr ? "Méthodologie" : "Methodology"}</span>
      </nav>
      <span className="kicker">{fr ? "Le calcul, en entier" : "The math, in full"}</span>
      <h1 className="vp-display mt-3 max-w-3xl text-[clamp(30px,5vw,52px)] font-bold uppercase leading-[1] tracking-[-0.03em]">
        {fr ? (
          <>
            Méthodologie <span className="hl">ultra-détaillée</span>
          </>
        ) : (
          <>
            The <span className="hl">full</span> methodology
          </>
        )}
      </h1>
      <p className="mt-4 max-w-2xl text-[16px] text-ink-2">
        {fr
          ? "Notre différenciateur est la transparence totale. Cette page documente chaque étape — données, fusion, modèle, validation, moteur — au niveau de détail d'un rapport technique."
          : "Our differentiator is total transparency. This page documents every step — data, merge, model, validation, engine — at technical-report depth."}
      </p>

      <div className="mt-10 space-y-8">
        {sections.map((sct) => (
          <section key={sct.num} className="vp-card p-6 sm:p-8">
            <div className="flex items-baseline gap-4">
              <span className="vp-mono text-[13px] font-bold text-green">{sct.num}</span>
              <h2 className="vp-display text-[20px] font-bold uppercase tracking-[-0.02em]">
                {sct.title}
              </h2>
            </div>
            <div className="mt-4 space-y-3.5">
              {sct.paras.map((p, i) => (
                <p key={i} className="max-w-3xl text-[14.5px] leading-relaxed text-ink-2">
                  {p}
                </p>
              ))}
            </div>
            {sct.kv && (
              <div className="mt-5 grid grid-cols-2 gap-2.5 sm:grid-cols-3">
                {sct.kv.map(([k, v]) => (
                  <div key={k} className="kv-cell">
                    <p className="k">{k}</p>
                    <p className="v">{v}</p>
                  </div>
                ))}
              </div>
            )}
            {sct.table && (
              <div className="src-wrap mt-5">
                <table className="src-table">
                  <thead>
                    <tr>
                      {sct.table.head.map((h) => (
                        <th key={h}>{h}</th>
                      ))}
                    </tr>
                  </thead>
                  <tbody>
                    {sct.table.rows.map(([a, b, c]) => (
                      <tr key={a}>
                        <td className="font-semibold">{a}</td>
                        <td className="vp-mono text-[12.5px]">{b}</td>
                        <td className="vp-mono text-[12.5px]">{c}</td>
                      </tr>
                    ))}
                  </tbody>
                </table>
              </div>
            )}
          </section>
        ))}
      </div>

      <p className="vp-mono mt-8 text-[11px] uppercase tracking-[0.06em] text-ink-3">
        {fr
          ? "Sources : rôles d'évaluation foncière du Québec (MAMH, données ouvertes) · IAAO Standard on Ratio Studies · IAAO Standard on AVMs"
          : "Sources: Québec property assessment rolls (MAMH, open data) · IAAO Standard on Ratio Studies · IAAO Standard on AVMs"}
      </p>
    </div>
  );
}
