"""Lesen/Schreiben der materials.csv und die gemeinsame BSDF-Auslesungslogik.

Frueher war das Auslesen der BSDF-Werte in ``add_material_to_csv`` und in
``update_material_in_csv`` doppelt vorhanden. Beide nutzen jetzt ``bsdf_to_row``.
"""

import csv
import os

from . import migration
from .material import FIELDS, HEADERS, COLUMN_COUNT, material_manager


def format_float(value, precision=3):
    """Rundet eine Gleitkommazahl auf eine feste Anzahl Dezimalstellen."""
    return f"{value:.{precision}f}"


def get_csv_path():
    addon_dir = os.path.dirname(os.path.realpath(__file__))
    return os.path.join(addon_dir, "materials.csv")


def reload(file_path=None):
    """Laedt die CSV neu in den MaterialManager.

    Eine v1.4-Datei (24 Spalten, alte Shader-Namen) wird vorher automatisch
    ins aktuelle Format migriert (Sicherung: materials.csv.bak-v14).
    """
    pfad = file_path or get_csv_path()
    material_manager.zuletzt_migriert = None
    if os.path.exists(pfad):
        material_manager.zuletzt_migriert = migration.migriere_falls_alt(pfad)
    material_manager.load_material_data(pfad)


def roughness_value(bsdf):
    """Roughness kann ueber den 'AddRoughness'-Math-Node (Imperfection) laufen."""
    socket = bsdf.inputs["Roughness"]
    if socket.is_linked:
        node = socket.links[0].from_node
        if node.type == "MATH" and node.label == "AddRoughness":
            return node.inputs[1].default_value
    return socket.default_value


def bsdf_to_row(bsdf, category, name):
    """Liest alle FIELDS aus einem Principled BSDF in eine CSV-Zeile (Liste von Strings)."""
    row = []
    for field in FIELDS:
        if field.attr == "category":
            row.append(category)
        elif field.attr == "name":
            row.append(name)
        elif field.attr == "roughness":
            row.append(format_float(roughness_value(bsdf)))
        elif field.kind == "float":
            row.append(format_float(bsdf.inputs[field.bsdf_input].default_value))
        else:  # vec
            values = bsdf.inputs[field.bsdf_input].default_value
            row.append(",".join(format_float(v) for v in values[: field.components]))
    return row


def append_material(bsdf, category, name):
    """Haengt ein neues Material an die CSV an und laedt sie neu."""
    path = get_csv_path()
    row = bsdf_to_row(bsdf, category, name)
    with open(path, "a", newline="", encoding="utf-8") as f:
        csv.writer(f).writerow(row)
    reload(path)
    return path


def update_material(name, bsdf):
    """Aktualisiert die Werte des Materials ``name`` in der CSV (Kategorie bleibt erhalten)."""
    path = get_csv_path()
    new_values = dict(zip(HEADERS, bsdf_to_row(bsdf, "", name)))
    rows = []
    updated = False
    with open(path, newline="", encoding="utf-8") as f:
        reader = csv.DictReader(f)
        fieldnames = reader.fieldnames
        for row in reader:
            if row.get("Name") == name:
                for key, value in new_values.items():
                    if key in ("Category", "Name"):
                        continue  # Identitaet nicht ueberschreiben
                    row[key] = value
                updated = True
            rows.append(row)
    if updated:
        with open(path, "w", newline="", encoding="utf-8") as f:
            writer = csv.DictWriter(f, fieldnames=fieldnames)
            writer.writeheader()
            writer.writerows(rows)
        print("CSV file updated successfully.")
    else:
        print(f"Material '{name}' not found in CSV.")
    reload(path)
    return updated


def delete_material(name, category):
    """Entfernt das Material ``name`` der Kategorie ``category`` aus der CSV."""
    path = get_csv_path()
    with open(path, newline="", encoding="utf-8") as f:
        reader = csv.reader(f)
        rows = [
            r
            for r in reader
            if not (r and len(r) > 1 and r[1] == name and r[0] == category)
        ]
    with open(path, "w", newline="", encoding="utf-8") as f:
        csv.writer(f).writerows(rows)
    reload(path)
