#!/usr/bin/env python3
import csv
import statistics
from pathlib import Path

FICHIER = Path(__file__).with_name("prix_produits.csv")

def charger():
    with FICHIER.open(encoding="utf-8", newline="") as f:
        return [{"id": r["product_id"], "x": float(r["poids_g"]), "y": float(r["prix_moyen"])} for r in csv.DictReader(f)]

def separer(lignes):
    test = [r for i, r in enumerate(lignes) if i % 5 == 0]
    train = [r for i, r in enumerate(lignes) if i % 5 != 0]
    return train, test

def entrainer(lignes):
    if not lignes:
        raise ValueError("L'entraînement ne contient aucune ligne")
    mx = sum(r["x"] for r in lignes) / len(lignes)
    my = sum(r["y"] for r in lignes) / len(lignes)
    variation_poids = sum((r["x"]-mx)**2 for r in lignes)
    if variation_poids == 0:
        raise ValueError("Il faut au moins deux poids différents pour apprendre la droite")
    pente = sum((r["x"]-mx)*(r["y"]-my) for r in lignes) / variation_poids
    return {"pente": pente, "origine": my-pente*mx, "moyenne": my,
            "mediane": statistics.median(r["y"] for r in lignes)}

def mae(lignes, prediction):
    return sum(abs(r["y"]-prediction(r)) for r in lignes) / len(lignes)

def main():
    lignes=charger(); train,test=separer(lignes); modele=entrainer(train)
    ids_train={r['id'] for r in train};ids_test={r['id'] for r in test}
    pred=lambda r: modele['origine']+modele['pente']*r['x']
    ref_moyenne=lambda r: modele['moyenne']
    ref_mediane=lambda r: modele['mediane']
    print(f"Lignes : {len(lignes)} ; entraînement : {len(train)} ; test : {len(test)}")
    print(f"Identifiants communs : {len(ids_train & ids_test)}")
    print(f"Référence moyenne : {modele['moyenne']:.2f} BRL")
    print(f"Référence médiane : {modele['mediane']:.2f} BRL")
    print(f"Modèle : prix = {modele['origine']:.2f} + {modele['pente']:.5f} × poids_g")
    print(f"MAE moyenne sur test : {mae(test,ref_moyenne):.2f} BRL")
    print(f"MAE médiane sur test : {mae(test,ref_mediane):.2f} BRL")
    print(f"MAE modèle sur test : {mae(test,pred):.2f} BRL")
    print(f"Plage poids entraînement : {min(r['x'] for r in train):.0f} à {max(r['x'] for r in train):.0f} g")
    print(f"Poids test hors plage : {sum(r['x'] < min(x['x'] for x in train) or r['x'] > max(x['x'] for x in train) for r in test)}")
    erreurs=sorted(
        ((abs(r['y']-pred(r)),r,pred(r)) for r in test),
        key=lambda resultat: (-resultat[0], resultat[1]['id']),
    )
    print("Trois erreurs absolues les plus grandes :")
    for e,r,p in erreurs[:3]: print(r['id'],f"réel={r['y']:.2f}",f"prédit={p:.2f}",f"erreur={e:.2f} BRL")
    assert len(ids_train)==len(train) and len(ids_test)==len(test) and not ids_train & ids_test
if __name__ == '__main__': main()
