import json
import math
import re
import unicodedata
from pathlib import Path

import pandas as pd


INPUT_PATH = Path(r"C:\Users\eorun\Desktop\modele_import_tarifs_depuis_sql.xlsx")
OUTPUT_PATH = Path(r"C:\wamp64\www\atelier-com-site\racine\atelier-com-platform\outputs\pricing_fill_full_grid\pricing_payload.json")


def cfg(curve, family, anchor_qty, anchor_price, note, actual_qtys=None, actual_totals=None):
    data = {
        "curve": curve,
        "family": family,
        "anchor_qty": anchor_qty,
        "anchor_price": float(anchor_price),
        "note": note,
    }
    if actual_qtys:
        data["actual_qtys"] = actual_qtys
    if actual_totals:
        data["actual_totals"] = [float(x) for x in actual_totals]
    return data


PRODUCT_CONFIG = {
    1: cfg("print100", "print_card", 100, 14.23, "Exaprint search: carte de visite papier classique des 14.23 EUR HT les 100 ex"),
    2: cfg("print1000", "print_leaflet", 1000, 34, "Realisaprint snippet: flyer des 34 EUR HT les 1000 ex"),
    3: cfg("print100", "print_leaflet", 100, 35, "Realisaprint snippet: depliant des 35 EUR HT les 100 ex"),
    5: cfg("print1000", "admin", 1000, 54, "Realisaprint snippet: enveloppe des 54 EUR HT les 1000 ex"),
    6: cfg("print100", "admin", 100, 50, "Realisaprint snippet: chemise a rabats des 50 EUR HT les 100 ex"),
    7: cfg("print1000", "print_card", 1000, 39, "Realisaprint snippet: carte postale des 39 EUR HT les 1000 ex"),
    8: cfg("print50", "print_card", 50, 25, "Realisaprint snippet: carte de correspondance des 25 EUR HT les 50 ex"),
    10: cfg("print100", "brochure", 100, 29, "Realisaprint snippet: brochure agrafee des 29 EUR HT les 100 ex"),
    11: cfg("print50", "brochure", 50, 61, "Realisaprint snippet: dos carre colle des 61 EUR HT les 50 ex"),
    13: cfg("print50", "print_leaflet", 50, 19, "Realisaprint snippet: affiche des 19 EUR HT les 50 ex"),
    14: cfg("print25", "calendar", 25, 33, "Realisaprint snippet: calendrier mural des 33 EUR HT les 25 ex"),
    15: cfg("print100", "calendar", 100, 184, "Realisaprint snippet: calendrier chevalet des 184 EUR HT les 100 ex"),
    16: cfg("print100", "office_pad", 100, 21, "Realisaprint snippet: bloc-notes encolle des 0.21 EUR HT l unite"),
    17: cfg("book20", "autocopiant", 20, 125, "Realisaprint snippet: carnet autocopiant des 125 EUR HT les 20 ex"),
    18: cfg("print1000", "restaurant_print", 1000, 76, "Realisaprint snippet: set de table des 76 EUR HT les 1000 ex"),
    19: cfg("print100", "restaurant_print", 100, 35, "Realisaprint snippet: menu a pli des 35 EUR HT les 100 ex"),
    20: cfg("office40", "office_pad", 40, 364, "Realisaprint snippet: sous-mains des 364 EUR HT les 40 ex"),
    26: cfg("sign1", "signage_stand", 1, 32, "Fulfiller search: roll up eco des 32 EUR HT"),
    27: cfg("sign1", "flag", 1, 79, "Standard source flag plume Fulfiller"),
    29: cfg("sign1", "signage_area", 1, 39, "Standard source bache 123baches"),
    31: cfg("sign1", "signage_area", 1, 18, "Standard source panneau forex 123panneaux"),
    34: cfg("sign1", "signage_area", 1, 32, "Standard source vitrophanie Realisaprint"),
    35: cfg("sign1", "signage_area", 1, 38, "Standard source vinyle microperfore Realisaprint"),
    36: cfg("sign1", "signage_area", 1, 29, "Standard source film depoli Realisaprint"),
    37: cfg("sign1", "signage_area", 1, 26, "Standard source kakemono suspendu Fulfiller"),
    38: cfg("sign1", "signage_stand", 1, 29, "Standard source x-banner Fulfiller"),
    39: cfg("sign1", "signage_stand", 1, 79, "Standard source stop-trottoir Fulfiller"),
    44: cfg("sign1", "signage_area", 1, 151, "Fulfiller related snippet: photocall grand format des 151 EUR HT"),
    45: cfg("sign1", "tablecloth", 1, 69, "Standard source nappe ronde Realisaprint"),
    62: cfg("actual", "wristband", 100, 70, "HelloPrint tiers visible pour bracelets Tyvek", [100, 250, 500, 1000, 2500, 5000], [70, 118, 193, 350, 825, 1550]),
    63: cfg("actual", "goodie", 250, 60, "ObjetRama tiers visibles pour tour de cou Lungo", [250, 500, 1000, 2500, 5000, 10000], [60, 115, 210, 450, 850, 1500]),
    67: cfg("sign1", "signage_stand", 1, 199, "Standard source comptoir d accueil Fulfiller"),
    68: cfg("sign1", "signage_stand", 1, 329, "Standard source stand parapluie textile Fulfiller"),
    69: cfg("sign1", "signage_stand", 1, 799, "Standard source arche gonflable Fulfiller"),
    78: cfg("goodie50", "goodie", 50, 295, "Fallback standard gourde ObjetRama"),
    79: cfg("actual", "goodie", 50, 318, "ObjetRama tiers visibles pour bouteille Astrio", [25, 50, 100, 250, 500, 1000], [168, 318, 623, 1423, 2705, 5160]),
    80: cfg("goodie50", "goodie", 50, 190, "Fallback standard cle USB ObjetRama"),
    86: cfg("goodie100", "goodie", 100, 129, "Fallback standard briquet Bic ObjetRama"),
    88: cfg("goodie50", "goodie", 50, 289, "Fallback standard lunch box ObjetRama"),
    103: cfg("sign1", "signage_stand", 1, 359, "Standard source stand parapluie courbe Fulfiller"),
    104: cfg("sign1", "signage_area", 1, 32, "Standard source panneau dibond 123panneaux"),
    105: cfg("sign1", "signage_area", 1, 14, "Standard source panneau akylux 123panneaux"),
    106: cfg("sign1", "flag", 1, 72, "Standard source flag goutte Fulfiller"),
    107: cfg("sign1", "tablecloth", 1, 79, "Standard source nappe rectangle Realisaprint"),
    108: cfg("print100", "print_card", 100, 74, "Realisaprint snippet: accroche-porte des 74 EUR HT les 100 ex"),
    109: cfg("print500", "label", 500, 50, "Ancrage utilisateur verifie le 2026-05-26: rond 5 cm polypro blanc mat sans finition 50 EUR HT les 500"),
    110: cfg("goodie25", "admin", 25, 95, "Saxoprint product page: classeurs de 25 a 1000 ex, fallback standard"),
    111: cfg("print100", "admin", 100, 53, "Realisaprint snippet: dossier administratif des 53 EUR HT les 100 ex"),
    112: cfg("print100", "print_card", 100, 18, "Realisaprint snippet: marque-page des 18 EUR HT les 100 ex"),
    113: cfg("print100", "office_pad", 100, 33, "Realisaprint snippet: post-it des 0.33 EUR HT l unite"),
    114: cfg("print1000", "admin", 1000, 32, "Realisaprint snippet: tete de lettre des 32 EUR HT les 1000 ex"),
    115: cfg("print100", "event_small", 100, 28, "Realisaprint snippet: badge epingle des 28 EUR HT les 100 ex"),
    116: cfg("goodie25", "goodie", 25, 289, "Fallback standard coussin ObjetRama"),
    117: cfg("goodie50", "goodie", 50, 149, "Fallback standard jeu de cartes ObjetRama"),
    118: cfg("goodie100", "goodie", 100, 119, "Fallback standard magnet frigo ObjetRama"),
    119: cfg("actual", "goodie", 100, 256, "ObjetRama tiers visibles pour mug ceramique", [80, 100, 140, 320, 500, 1000], [212, 256, 346, 739, 1085, 2110]),
    120: cfg("goodie50", "goodie", 50, 279, "Fallback standard mug isotherme ObjetRama"),
    121: cfg("goodie25", "goodie", 25, 399, "Source incoherente parapluie -> fallback standard powerbank"),
    122: cfg("goodie250", "goodie", 250, 115, "Fallback standard sac papier kraft ObjetRama"),
    123: cfg("actual", "goodie", 500, 70, "ObjetRama tiers visibles pour stylo Riocolor", [500, 1000, 2500, 5000, 10000, 15000], [70, 130, 300, 550, 1000, 1350]),
    124: cfg("goodie50", "goodie", 50, 189, "Fallback standard tapis de souris ObjetRama"),
    125: cfg("goodie50", "goodie", 50, 129, "Fallback standard tote-bag ObjetRama"),
    126: cfg("actual", "goodie", 250, 60, "ObjetRama tiers visibles pour tour de cou Lungo", [250, 500, 1000, 2500, 5000, 10000], [60, 115, 210, 450, 850, 1500]),
    127: cfg("goodie5", "goodie_large", 5, 495, "Source incoherente pizza -> fallback standard transat chilienne"),
    128: cfg("print500", "packaging", 500, 185, "Fallback standard boite burger Realisaprint"),
    129: cfg("goodie250", "packaging", 250, 289, "Fallback standard coffret bouteille Realisaprint"),
    130: cfg("print500", "packaging", 500, 89, "Fallback standard gobelet carton Realisaprint"),
    131: cfg("goodie250", "packaging", 250, 155, "Fallback standard gobelet reutilisable Realisaprint"),
    132: cfg("goodie50", "goodie", 50, 145, "Fallback standard rond de serviette ObjetRama"),
    133: cfg("print500", "packaging", 500, 29, "Realisaprint snippet: sous-bock des 0.04 EUR HT l unite, grille standardisee"),
    134: cfg("sign1", "signage_area", 1, 119, "Standard source stand tubulaire Fulfiller"),
}


CURVES = {
    "print25": ([25, 50, 100, 250, 500, 1000], [1.00, 1.48, 2.18, 4.10, 7.55, 13.90]),
    "print50": ([25, 50, 100, 250, 500, 1000], [0.68, 1.00, 1.62, 2.95, 5.45, 10.10]),
    "print100": ([25, 50, 100, 250, 500, 1000], [0.55, 0.72, 1.00, 1.82, 3.20, 5.85]),
    "print500": ([100, 250, 500, 1000, 2500, 5000], [0.58, 0.80, 1.00, 1.40, 2.46, 4.34]),
    "print1000": ([100, 250, 500, 1000, 2500, 5000], [0.45, 0.63, 0.79, 1.00, 1.82, 3.12]),
    "book20": ([10, 20, 50, 100, 250, 500], [0.72, 1.00, 2.08, 3.92, 9.20, 17.40]),
    "office40": ([10, 25, 40, 100, 250, 500], [0.44, 0.73, 1.00, 2.14, 4.75, 8.95]),
    "sign1": ([1, 2, 5, 10, 25, 50], [1.00, 1.72, 4.00, 7.50, 17.80, 33.50]),
    "goodie5": ([5, 10, 25, 50, 100, 250], [1.00, 1.82, 4.10, 7.55, 14.10, 32.20]),
    "goodie25": ([10, 25, 50, 100, 250, 500], [0.56, 1.00, 1.78, 3.35, 7.20, 13.40]),
    "goodie50": ([25, 50, 100, 250, 500, 1000], [0.72, 1.00, 1.82, 4.10, 7.60, 14.20]),
    "goodie100": ([50, 100, 250, 500, 1000, 2500], [0.62, 1.00, 2.30, 4.20, 7.90, 18.70]),
    "goodie250": ([100, 250, 500, 1000, 2500, 5000], [0.56, 1.00, 1.82, 3.35, 7.60, 14.40]),
}


A_FORMATS = {
    "a0": (84.1, 118.9),
    "a1": (59.4, 84.1),
    "a2": (42.0, 59.4),
    "a3": (29.7, 42.0),
    "a4": (21.0, 29.7),
    "a5": (14.8, 21.0),
    "a6": (10.5, 14.8),
}


BASE_COLUMNS = {
    "product_id",
    "sku",
    "Nom produit",
    "product_type",
    "Catégorie",
    "Sous-catégorie",
    "Clé combinaison",
    "Configuration lisible",
    "Attributs techniques JSON",
    "base_spec_json",
    "Source",
    "Quantité",
    "Coût achat HT",
    "Notes import",
}


def normalize_text(value):
    text = str(value or "")
    text = unicodedata.normalize("NFKD", text)
    text = "".join(ch for ch in text if not unicodedata.combining(ch))
    text = text.lower()
    return text.strip()


def cell_text(row, key):
    value = row.get(key, "")
    if value is None:
        return ""
    if isinstance(value, float) and math.isnan(value):
        return ""
    return str(value).strip()


def first_number(text):
    nums = re.findall(r"\d+(?:[.,]\d+)?", text)
    if not nums:
        return None
    return float(nums[0].replace(",", "."))


def all_numbers(text):
    return [float(x.replace(",", ".")) for x in re.findall(r"\d+(?:[.,]\d+)?", text)]


def parse_format_metric(text):
    norm = normalize_text(text)
    if not norm:
        return None
    if "taille < 1 m2" in norm or "taille<1m2" in norm:
        return 0.75
    if "1 m2 < taille < 2 m2" in norm or "1m2 < taille < 2m2" in norm:
        return 1.50
    if "2 m2 < taille < 3 m2" in norm or "2m2 < taille < 3m2" in norm:
        return 2.50
    if "+ plus" in norm:
        return 4.00
    for code, dims in A_FORMATS.items():
        if code in norm:
            return dims[0] * dims[1]
    nums = all_numbers(norm)
    if not nums:
        return None
    if "cl" in norm or "go" in norm:
        return max(nums)
    if "mm" in norm:
        nums = [n / 10.0 for n in nums]
    elif re.search(r"(?<![a-z])m(?![a-z])", norm) and "m2" not in norm:
        nums = [n * 100.0 for n in nums]
    if "m2" in norm and len(nums) == 1:
        return nums[0]
    if len(nums) >= 2:
        if "rond" in norm or "ronde" in norm:
            return nums[0] * nums[0]
        return nums[0] * nums[1]
    if "rond" in norm or "ronde" in norm or "carre" in norm:
        return nums[0] * nums[0]
    return nums[0]


def parse_row_metric(row):
    for key in ("format", "largeur", "hauteur", "capacite"):
        metric = parse_format_metric(cell_text(row, key))
        if metric:
            return metric
    return None


def parse_spec_metric(row):
    pieces = [
        cell_text(row, "support-matiere"),
        cell_text(row, "support-mati-ere"),
        cell_text(row, "grammage-epaisseur"),
        cell_text(row, "capacite"),
        cell_text(row, "type"),
        cell_text(row, "support-mati-ere"),
    ]
    values = []
    for piece in pieces:
        norm = normalize_text(piece)
        if not norm:
            continue
        nums = all_numbers(norm)
        if not nums:
            continue
        values.append(max(nums))
    return max(values) if values else None


def parse_pages_value(row):
    values = []
    for key in ("nbr-pages", "nbr-pages-interieures"):
        text = cell_text(row, key)
        if not text:
            continue
        nums = all_numbers(text)
        if nums:
            values.append(max(nums))
        elif "plus" in normalize_text(text):
            values.append(32)
    return max(values) if values else None


def parse_sheet_value(row):
    values = []
    for key in ("nbr-feuillets", "nbr-feuilles"):
        nums = all_numbers(cell_text(row, key))
        if nums:
            values.append(max(nums))
    return max(values) if values else None


def parse_bundle_value(row):
    nums = all_numbers(cell_text(row, "grammage-epaisseur"))
    if nums and "liasse" in normalize_text(cell_text(row, "grammage-epaisseur")):
        return max(nums)
    return None


def material_factor(row, family):
    text = " ".join(
        [
            cell_text(row, "support-matiere"),
            cell_text(row, "support-mati-ere"),
            cell_text(row, "matiere"),
            cell_text(row, "grammage-epaisseur"),
            cell_text(row, "type"),
        ]
    )
    norm = normalize_text(text)
    factor = 1.0
    if "recycle" in norm:
        factor *= 1.04
    if "secur" in norm:
        factor *= 1.10
    if "transparent" in norm:
        factor *= 1.06
    if "textile stretch" in norm:
        factor *= 1.18
    if "premium" in norm:
        factor *= 1.05
    if "pro 450" in norm or "pvc 500" in norm:
        factor *= 1.12
    if "microperfore" in norm:
        factor *= 1.12
    if "depoli" in norm:
        factor *= 1.10
    if "forex 5" in norm:
        factor *= 1.12
    if "aludibon 4" in norm:
        factor *= 1.12
    if "aludibon 6" in norm:
        factor *= 1.20
    if "akylux 4.5" in norm:
        factor *= 1.10
    if "carton micro cannele 3" in norm:
        factor *= 1.14
    if "vinyle souple 2mm" in norm:
        factor *= 1.10
    if "polypro translucide" in norm:
        factor *= 1.05
    if "polypro opaque" in norm:
        factor *= 1.02
    if "soft touch" in norm and family in {"admin", "print_card"}:
        factor *= 1.04
    return factor


def impression_factor(row, family):
    norm = normalize_text(cell_text(row, "impression"))
    factor = 1.0
    if "recto / verso" in norm or "recto verso" in norm or "verso" in norm:
        factor *= 1.65 if family in {"signage_area", "signage_stand"} else 1.12
    if "1 couleur" in norm:
        factor *= 0.92
    if "noire" in norm:
        factor *= 0.86
    if "non imprime" in norm:
        factor *= 0.88
    return factor


def finish_factor(row):
    parts = [
        cell_text(row, "finition"),
        cell_text(row, "pelliculage"),
        cell_text(row, "lamination"),
        cell_text(row, "faconnage-option"),
        cell_text(row, "oeillets"),
        cell_text(row, "ourlet"),
        cell_text(row, "ganse"),
        cell_text(row, "embase"),
        cell_text(row, "gonfleur"),
        cell_text(row, "rainage"),
        cell_text(row, "visuel"),
        cell_text(row, "grammage-epaisseur"),
    ]
    norm = normalize_text(" ".join(parts))
    factor = 1.0
    if "vernis 3d" in norm:
        factor *= 1.18
    elif "vernis selectif" in norm:
        factor *= 1.14
    elif "vernis uv" in norm or "vernis brillant" in norm or "vernis mat" in norm:
        factor *= 1.08
    if "dorure" in norm:
        factor *= 1.20
    if "pelliculage" in norm:
        factor *= 1.12
    if "soft touch" in norm or "soft-touch" in norm:
        factor *= 1.16
    if "anti eraflures" in norm or "anti-rayure" in norm:
        factor *= 1.14
    if "brillant recto verso" in norm or "mat recto verso" in norm:
        factor *= 1.10
    if "double rainage" in norm:
        factor *= 1.05
    if "oeillet" in norm and "aucune" not in norm:
        factor *= 1.08
    if "ourlet" in norm and "aucune" not in norm:
        factor *= 1.07
    if "embase lestable" in norm:
        factor *= 1.18
    elif "platine" in norm:
        factor *= 1.12
    elif "pied parasol" in norm:
        factor *= 1.10
    if "gonfleur" in norm and "sans" not in norm:
        factor *= 1.12
    if "spot" in norm:
        factor *= 1.15
    if "etagere" in norm:
        factor *= 1.08
    if "2 visuels" in norm:
        factor *= 1.14
    if "3 visuels" in norm:
        factor *= 1.22
    return factor


def family_exponents(family):
    if family in {"signage_area", "signage_stand", "flag", "tablecloth", "goodie_large"}:
        return 0.90, 0.14, 0.03, 0.12, 0.18
    if family in {"brochure", "autocopiant", "calendar"}:
        return 0.50, 0.12, 0.10, 0.20, 0.24
    if family in {"goodie", "wristband"}:
        return 0.28, 0.10, 0.02, 0.02, 0.03
    if family in {"packaging", "restaurant_print"}:
        return 0.42, 0.10, 0.04, 0.06, 0.04
    return 0.38, 0.10, 0.05, 0.10, 0.10


def build_row_features(row, family):
    return {
        "size_metric": parse_row_metric(row),
        "spec_metric": parse_spec_metric(row),
        "pages_value": parse_pages_value(row),
        "sheet_value": parse_sheet_value(row),
        "bundle_value": parse_bundle_value(row),
        "material_factor": material_factor(row, family),
        "impression_factor": impression_factor(row, family),
        "finish_factor": finish_factor(row),
    }


def min_positive(values):
    values = [v for v in values if v and v > 0]
    return min(values) if values else None


def row_score(feat, mins, family):
    size_exp, spec_weight, page_weight, sheet_weight, bundle_weight = family_exponents(family)
    score = feat["material_factor"] * feat["impression_factor"] * feat["finish_factor"]
    if feat["size_metric"] and mins["size"]:
        score *= max(feat["size_metric"] / mins["size"], 1.0) ** size_exp
    if feat["spec_metric"] and mins["spec"]:
        score *= 1.0 + spec_weight * max((feat["spec_metric"] / mins["spec"]) - 1.0, 0.0)
    if feat["pages_value"] and mins["pages"]:
        score *= 1.0 + page_weight * max((feat["pages_value"] / mins["pages"]) - 1.0, 0.0)
    if feat["sheet_value"] and mins["sheet"]:
        score *= 1.0 + sheet_weight * max((feat["sheet_value"] / mins["sheet"]) - 1.0, 0.0)
    if feat["bundle_value"] and mins["bundle"]:
        score *= 1.0 + bundle_weight * max((feat["bundle_value"] / mins["bundle"]) - 1.0, 0.0)
    return score


def format_number(value):
    rounded = round(float(value))
    return str(int(rounded))


def build_prices(config, score_ratio):
    if config["curve"] == "actual":
        qtys = config["actual_qtys"]
        totals = [max(round(x * score_ratio), 1) for x in config["actual_totals"]]
        return qtys, totals
    qtys, mults = CURVES[config["curve"]]
    totals = [max(round(config["anchor_price"] * score_ratio * mult), 1) for mult in mults]
    return qtys, totals


def fallback_config(row):
    category = normalize_text(row.get("Catégorie", ""))
    name = normalize_text(row.get("Nom produit", ""))
    source = normalize_text(row.get("Source", ""))
    if "objetrama" in source:
        return cfg("goodie50", "goodie", 50, 199, "Fallback standard ObjetRama")
    if "fulfiller" in source or "123" in source:
        return cfg("sign1", "signage_area", 1, 59, "Fallback standard grand format")
    if "realisaprint" in source or "exaprint" in source or "saxoprint" in source:
        if "brochure" in name or "catalogue" in name:
            return cfg("print100", "brochure", 100, 39, "Fallback standard brochure")
        return cfg("print1000", "print_leaflet", 1000, 39, "Fallback standard print")
    if "papeterie" in category:
        return cfg("print1000", "print_leaflet", 1000, 39, "Fallback papeterie")
    if "signaletique" in category or "evenementiel" in category:
        return cfg("sign1", "signage_area", 1, 59, "Fallback signaletique")
    return cfg("goodie50", "goodie", 50, 199, "Fallback standard")


def main():
    df = pd.read_excel(INPUT_PATH, sheet_name="Import_Tarifs")
    attr_cols = [c for c in df.columns if c not in BASE_COLUMNS]
    updates = []

    for product_id, sub in df.groupby("product_id", sort=True):
        pid = int(product_id)
        config = PRODUCT_CONFIG.get(pid) or fallback_config(sub.iloc[0].to_dict())
        rows = [sub.loc[idx].to_dict() for idx in sub.index]
        features = [build_row_features(row, config["family"]) for row in rows]
        mins = {
            "size": min_positive([feat["size_metric"] for feat in features]),
            "spec": min_positive([feat["spec_metric"] for feat in features]),
            "pages": min_positive([feat["pages_value"] for feat in features]),
            "sheet": min_positive([feat["sheet_value"] for feat in features]),
            "bundle": min_positive([feat["bundle_value"] for feat in features]),
        }
        raw_scores = [row_score(feat, mins, config["family"]) for feat in features]
        min_score = min(raw_scores) if raw_scores else 1.0

        for idx, row, raw_score in zip(sub.index, rows, raw_scores):
            qtys, totals = build_prices(config, raw_score / min_score)
            quantity_text = ";".join(str(int(x)) for x in qtys)
            cost_text = ";".join(format_number(x) for x in totals)
            note = f"grille IA | {config['note']} | caracteristiques tableau prioritaires, fallback standard si source dynamique absente"
            updates.append(
                {
                    "row_number": int(idx) + 2,
                    "quantity_text": quantity_text,
                    "cost_text": cost_text,
                    "note_text": note,
                    "product_id": pid,
                    "sku": row["sku"],
                    "name": row["Nom produit"],
                }
            )

    OUTPUT_PATH.parent.mkdir(parents=True, exist_ok=True)
    OUTPUT_PATH.write_text(json.dumps(updates, ensure_ascii=False, indent=2), encoding="utf-8")
    print(json.dumps({"output": str(OUTPUT_PATH), "rows": len(updates)}, ensure_ascii=False))


if __name__ == "__main__":
    main()
