"""Notation d'un module : renormalisation par archétype + verdict.

Principe : seuls les critères APPLICABLES à l'archétype du module comptent. Le
dénominateur est la somme de leurs poids (jamais 100 en dur) ; ainsi un module
de contrat (sans hand-off) n'est pas pénalisé pour un artefact out/ qu'il n'a
jamais vocation à produire. La note est renormalisée sur 100 puis arrondie.

  note = round(100 * Σ poids(critères applicables PASS) / Σ poids(applicables))

Un module PASS ssi note ≥ pass_score (95 · CLAUDE.md #5).

Déterministe : arrondi « demi vers le pair » interdit (dépend de la plateforme) →
on utilise un arrondi explicite demi-supérieur, stable et reproductible.
"""

from __future__ import annotations

import os

from . import criteria
from .deps import DELIVERABLES_ROOT


def _round_half_up(x: float) -> int:
    return int(x + 0.5)


def _weight_of(spec: dict, cid: str) -> int:
    for c in spec["criteria"]:
        if c["id"] == cid:
            return c["weight"]
    raise KeyError(f"Poids introuvable pour le critère {cid}")


def score_module(spec: dict, module: dict) -> dict:
    thresholds = spec["thresholds"]
    archetype = module["archetype"]
    applicable = spec["archetypes"][archetype]["applicable"]
    mod_dir = os.path.join(DELIVERABLES_ROOT, module["path"])

    if not os.path.isdir(mod_dir):
        raise FileNotFoundError(
            f"Module déclaré introuvable sur disque : {module['path']}")

    checks = []
    earned = 0
    total = 0
    for cid in applicable:
        weight = _weight_of(spec, cid)
        res = criteria.evaluate(mod_dir, cid, thresholds)
        total += weight
        if res["passed"]:
            earned += weight
        checks.append({
            "criterion": cid,
            "weight": weight,
            "passed": res["passed"],
            "evidence": res["evidence"],
        })

    if total == 0:
        raise ValueError(
            f"Archétype {archetype} sans critère applicable — spec incohérent.")

    score = _round_half_up(100.0 * earned / total)
    verdict = "PASS" if score >= thresholds["pass_score"] else "FAIL"
    return {
        "id": module["id"],
        "path": module["path"],
        "sprint": module["sprint"],
        "archetype": archetype,
        "applicable_weight": total,
        "earned_weight": earned,
        "score": score,
        "verdict": verdict,
        "checks": checks,
    }


def score_all(spec: dict) -> list[dict]:
    # Ordre stable : par sprint puis par chemin (déterminisme du hand-off).
    mods = sorted(spec["modules"], key=lambda m: (m["sprint"], m["path"]))
    return [score_module(spec, m) for m in mods]
