"""Détection de cases à cocher par analyse de densité de pixels sombres.

GLM-OCR ne sait pas distinguer une case cochée d'une case vide (A/B test, cf.
test_prompt_crop_v2.py). On reprend l'approche déterministe du pipeline legacy :
cropper la case à coord relative, compter les pixels sombres, comparer les
densités entre les deux cases pour trancher.
"""
from dataclasses import dataclass
from pathlib import Path
import numpy as np
from PIL import Image

DARK_THRESHOLD = 128      # pixels < 128 (0-255) comptés comme sombres
AMBIGU_MARGIN = 0.01      # différence minimale de densité pour trancher
                           # (calibré pour couvrir OGC 69 où diff=+0.010)
INNER_FRAC = 0.35         # on exclut 35% de marge pour ignorer le cadre et
                           # n'analyser que le centre de la case (calibré sur
                           # OGC 7/27/55/86 : 4/4 corrects)


@dataclass
class CheckboxZones:
    """Coordonnées relatives (x1,y1,x2,y2) dans [0,1] des deux cases."""
    accord: tuple[float, float, float, float]
    desaccord: tuple[float, float, float, float]


# Zones englobant la case entière (cadre compris). On utilisera INNER_FRAC
# pour n'analyser que le centre et ignorer le cadre.
RECUEIL_ACCORD_DESACCORD = CheckboxZones(
    accord=    (0.588, 0.838, 0.622, 0.860),
    desaccord= (0.588, 0.858, 0.622, 0.880),
)

# Zones pour la page concertation_2 (maintien / retour / autre groupage)
# À calibrer si besoin plus tard
CONCERTATION_2_DECISION = CheckboxZones(
    accord=    (0.035, 0.270, 0.060, 0.290),  # maintien avis contrôleur
    desaccord= (0.280, 0.270, 0.305, 0.290),  # retour groupage DIM
)


def dark_ratio(image: Image.Image, zone: tuple[float, float, float, float],
               inner_frac: float = INNER_FRAC) -> float:
    """Fraction de pixels sombres au centre de la zone (cadre exclu).

    On crope la zone puis on retire une marge de `inner_frac` de chaque côté
    pour ne garder que le contenu central (croix éventuelle).
    """
    w, h = image.size
    x1, y1, x2, y2 = zone
    px1, py1 = int(x1*w), int(y1*h)
    px2, py2 = int(x2*w), int(y2*h)
    dx = int((px2 - px1) * inner_frac)
    dy = int((py2 - py1) * inner_frac)
    crop = image.crop((px1 + dx, py1 + dy, px2 - dx, py2 - dy))
    gray = np.array(crop.convert("L"))
    return float(np.mean(gray < DARK_THRESHOLD))


def detect_accord_desaccord(
    image_path: str | Path,
    zones: CheckboxZones = RECUEIL_ACCORD_DESACCORD,
) -> dict:
    """Retourne la décision et les ratios bruts pour debug/audit.

    Convention : "accord" si la case Accord est plus remplie, "désaccord" si
    la case Désaccord est plus remplie, "ambigu" si l'écart est trop faible.
    """
    img = Image.open(image_path)
    r_acc = dark_ratio(img, zones.accord)
    r_des = dark_ratio(img, zones.desaccord)
    diff = r_acc - r_des

    if abs(diff) < AMBIGU_MARGIN:
        decision = "ambigu"
    elif diff > 0:
        decision = "accord"
    else:
        decision = "désaccord"

    return {
        "decision": decision,
        "ratio_accord": round(r_acc, 4),
        "ratio_desaccord": round(r_des, 4),
        "diff": round(diff, 4),
    }
