#!/usr/bin/env python3
"""Calcul d'un forfait IteraGPU complet en USD, Python >= 3.10, bibliothèque standard, MIT."""
import argparse
import csv
import json
import sys
from decimal import Decimal, localcontext
from pathlib import Path


def load_tariffs(path=None):
    source = Path(path) if path else Path(__file__).with_name("tarifs-forfaits.csv")
    rows = {}
    with source.open(encoding="utf-8", newline="") as handle:
        for row in csv.DictReader(handle):
            key = (row["product_id"], int(row["duration_days"]))
            if key in rows:
                raise ValueError("Tarif dupliqué dans le CSV.")
            price = int(row["price_per_lot_usd_minor"])
            count = int(row["gpu_per_lot"])
            if key[1] not in (3, 7, 30) or price <= 0 or count <= 0 or row["currency"] != "USD":
                raise ValueError("Tarif invalide dans le CSV.")
            rows[key] = dict(row, price_per_lot_usd_minor=price, gpu_per_lot=count)
    return rows


def calculate(gpu, days, lots, accepted_results=None, tariffs_path=None):
    if isinstance(lots, bool) or not isinstance(lots, int) or not 1 <= lots <= 10:
        raise ValueError("lots doit être un entier de 1 à 10 ; la disponibilité reste à vérifier.")
    if isinstance(days, bool) or days not in (3, 7, 30):
        raise ValueError("days doit valoir 3, 7 ou 30.")
    if accepted_results is not None and (isinstance(accepted_results, bool)
            or not isinstance(accepted_results, int) or accepted_results <= 0):
        raise ValueError("accepted-results doit être un entier strictement positif, ou être omis.")
    tariffs = load_tariffs(tariffs_path)
    if (gpu, days) not in tariffs:
        raise ValueError("Modèle inconnu : consulter product_id dans tarifs-forfaits.csv.")
    row = tariffs[(gpu, days)]
    total = row["price_per_lot_usd_minor"] * lots
    with localcontext() as ctx:
        ctx.prec = 40
        usd = Decimal(total) / 100
        unit = str(usd / accepted_results) if accepted_results is not None else None
        return {
            "product_id": gpu, "model": row["model"], "currency": "USD", "duration_days": days,
            "lots": lots, "gpu_per_lot": row["gpu_per_lot"], "gpu_total": row["gpu_per_lot"] * lots,
            "price_per_lot_usd_minor": row["price_per_lot_usd_minor"],
            "package_total_usd_minor": total, "package_total_usd": format(usd, ".2f"),
            "accepted_results": accepted_results, "cost_per_accepted_result_usd": unit,
            "tariff_snapshot_date": row["snapshot_date"],
            "scope": "Prix du forfait entier multiplié par les lots, sans prorata horaire. "
                     "Un lot B200 contient deux GPU : son prix n'est pas multiplié une seconde fois. "
                     "La quantité utile est fournie après validation qualité, jamais déduite de la vitesse. "
                     "Les répétitions d'un même corpus ne sont pas des corpus utiles supplémentaires. "
                     "Ce calcul ne réserve aucun stock et ne constitue pas une commande.",
        }


def main(argv=None):
    parser = argparse.ArgumentParser(description=__doc__)
    parser.add_argument("--gpu", required=True, help="product_id du CSV")
    parser.add_argument("--days", required=True, type=int, choices=(3, 7, 30))
    parser.add_argument("--lots", required=True, type=int)
    parser.add_argument("--accepted-results", type=int, help="Unités utiles distinctes, validées ; sans défaut")
    args = parser.parse_args(argv)
    try:
        print(json.dumps(calculate(args.gpu, args.days, args.lots, args.accepted_results),
                         ensure_ascii=False, indent=2))
        return 0
    except (ValueError, OSError, KeyError) as error:
        print(f"Calcul non produit : {error}", file=sys.stderr)
        return 2


if __name__ == "__main__":
    raise SystemExit(main())
