"""
Esportazione Excel per progetti PIF.

Genera un file Excel con 4 fogli:
1. Anagrafica - Informazioni ordine e lista ingredienti
2. Esposizione - Parametri di esposizione
3. SED - Calcolo Systemic Exposure Dosage (senza DAP)
4. MoS - Calcolo Margin of Safety (con DAP, indicatore tossicologico, restrizioni)
"""

import os
from openpyxl import Workbook
from openpyxl.styles import Font, Alignment, PatternFill, Border, Side, numbers
from openpyxl.utils import get_column_letter

from pif_compiler.functions.common_log import get_logger

logger = get_logger()

# ==================== STYLES ====================

TITLE_FONT = Font(bold=True, size=14)
LABEL_FONT = Font(bold=True, size=11)
HEADER_FONT = Font(bold=True, color="FFFFFF", size=11)
HEADER_FILL = PatternFill(start_color="4472C4", end_color="4472C4", fill_type="solid")
LIGHT_FILL = PatternFill(start_color="D9E2F3", end_color="D9E2F3", fill_type="solid")
WARNING_FILL = PatternFill(start_color="FFC7CE", end_color="FFC7CE", fill_type="solid")
THIN_BORDER = Border(
    left=Side(style='thin'),
    right=Side(style='thin'),
    top=Side(style='thin'),
    bottom=Side(style='thin')
)


def _style_header_row(ws, row, num_cols):
    """Applica stile alle celle header."""
    for col in range(1, num_cols + 1):
        cell = ws.cell(row=row, column=col)
        cell.font = HEADER_FONT
        cell.fill = HEADER_FILL
        cell.alignment = Alignment(horizontal='center', vertical='center', wrap_text=True)
        cell.border = THIN_BORDER


def _apply_borders(ws, row, num_cols):
    """Applica bordi a una riga."""
    for col in range(1, num_cols + 1):
        ws.cell(row=row, column=col).border = THIN_BORDER


WRAP_ALIGNMENT = Alignment(vertical='top', wrap_text=True)
WRAP_CENTER = Alignment(horizontal='center', vertical='top', wrap_text=True)


def _set_column_widths(ws, widths):
    """Imposta larghezze colonne fisse e applica wrap_text a tutte le celle con dati."""
    for i, w in enumerate(widths, 1):
        ws.column_dimensions[get_column_letter(i)].width = w
    for row in ws.iter_rows(min_row=1, max_row=ws.max_row, max_col=len(widths)):
        for cell in row:
            if cell.alignment.horizontal == 'center':
                cell.alignment = Alignment(horizontal='center', vertical='top', wrap_text=True)
            else:
                cell.alignment = WRAP_ALIGNMENT


def _get_ingredient_name(pi):
    """Restituisce il nome migliore per un ingrediente."""
    if pi.inci:
        return pi.inci
    if pi.ingredient and pi.ingredient.inci:
        return pi.ingredient.inci[0]
    return pi.cas or ""


def _get_cosing_restrictions(ingredient):
    """Estrae le restrizioni COSING da un ingrediente."""
    annex = []
    other = []
    if ingredient and ingredient.cosing_info:
        for cosing in ingredient.cosing_info:
            annex.extend(cosing.annex)
            other.extend(cosing.otherRestrictions)
    return "; ".join(annex), "; ".join(other)


def _get_dap_info_text(ingredient):
    """Formatta le informazioni DAP come testo leggibile."""
    if not ingredient or not ingredient.dap_info:
        return ""
    d = ingredient.dap_info
    parts = []
    if d.molecular_weight is not None:
        parts.append(f"Peso Molecolare: {d.molecular_weight} Da")
    if d.log_pow is not None:
        parts.append(f"LogP: {d.log_pow}")
    if d.tpsa is not None:
        parts.append(f"TPSA: {d.tpsa} A\u00b2")
    if d.melting_point is not None:
        parts.append(f"Punto di Fusione: {d.melting_point}\u00b0C")
    if d.high_ionization is not None:
        parts.append(f"pKa: {d.high_ionization}")
    parts.append(f"DAP: {d.dap_value * 100:.0f}%")
    return ", ".join(parts)


# ==================== SHEET BUILDERS ====================

def _build_anagrafica(wb, project):
    """Sheet 1: Anagrafica e lista ingredienti."""
    ws = wb.active
    ws.title = "Anagrafica"

    ws.merge_cells('A1:E1')
    ws['A1'] = "INFORMAZIONI ORDINE"
    ws['A1'].font = TITLE_FONT

    info_rows = [
        ("Cliente", project.client_name),
        ("Nome Prodotto", project.product_name),
        ("Preset Esposizione", project.esposition.preset_name),
    ]
    for i, (label, value) in enumerate(info_rows):
        ws.cell(row=i + 3, column=1, value=label).font = LABEL_FONT
        ws.cell(row=i + 3, column=2, value=value)

    # Tabella ingredienti
    tbl_row = 7
    headers = [
        "INCI",
        "CAS",
        "Percentuale (%)",
        "Colorante",
        "Escluso da Valutazione Tossicologica"
    ]
    for col, h in enumerate(headers, 1):
        ws.cell(row=tbl_row, column=col, value=h)
    _style_header_row(ws, tbl_row, len(headers))

    for i, pi in enumerate(project.ingredients):
        r = tbl_row + 1 + i
        ws.cell(row=r, column=1, value=pi.inci or "")
        ws.cell(row=r, column=2, value=pi.cas or "")
        ws.cell(row=r, column=3, value=pi.percentage)
        ws.cell(row=r, column=4, value="Si" if pi.is_colorante else "No")
        ws.cell(row=r, column=5, value="Si" if pi.skip_tox else "No")
        _apply_borders(ws, r, len(headers))

    _set_column_widths(ws, [20, 14, 14, 12, 18])


def _build_esposizione(wb, project):
    """
    Sheet 2: Parametri di esposizione.

    Layout delle celle di riferimento (usate nelle formule SED/MoS):
      B5  = Peso Corporeo Target (kg)
      B12 = Quantita Giornaliera (g/giorno)
      B13 = Fattore di Ritenzione
    """
    ws = wb.create_sheet("Esposizione")
    esp = project.esposition

    ws.merge_cells('A1:B1')
    ws['A1'] = "PARAMETRI DI ESPOSIZIONE"
    ws['A1'].font = TITLE_FONT

    params = [
        # label, value, row (starting from 3)
        ("Tipo Prodotto", esp.tipo_prodotto),
        ("Popolazione Target", esp.popolazione_target),
        ("Peso Corporeo Target (kg)", esp.peso_target_kg),              # B5
        ("Luogo di Applicazione", esp.luogo_applicazione),
        ("Vie di Esposizione Normali", ", ".join(esp.esp_normali)),
        ("Vie di Esposizione Secondarie", ", ".join(esp.esp_secondarie)),
        ("Vie di Esposizione Nano", ", ".join(esp.esp_nano)),
        ("Superficie Esposta (cm\u00b2)", esp.sup_esposta),
        ("Frequenza di Applicazione (applicazioni/giorno)", esp.freq_applicazione),
        ("Quantita Giornaliera (g/giorno)", esp.qta_giornaliera),       # B12
        ("Fattore di Ritenzione", esp.ritenzione),                      # B13
    ]

    for i, (label, value) in enumerate(params):
        row = i + 3
        ws.cell(row=row, column=1, value=label).font = LABEL_FONT
        ws.cell(row=row, column=2, value=value)

    # Campi calcolati con formule Excel
    ws.cell(row=15, column=1, value="Esposizione Calcolata (g/giorno)").font = LABEL_FONT
    ws['B15'] = "=B12*B13"
    ws['B15'].number_format = '0.0000'

    ws.cell(row=16, column=1, value="Esposizione Relativa (mg/kg bw/giorno)").font = LABEL_FONT
    ws['B16'] = "=B15*1000/B5"
    ws['B16'].number_format = '0.0000'

    ws.column_dimensions['A'].width = 50
    ws.column_dimensions['B'].width = 25


def _build_sed(wb, project):
    """
    Sheet 3: Calcolo SED senza DAP, con restrizioni COSING.

    Formula SED (mg/kg bw/giorno):
      = (percentuale / 100) * qta_giornaliera * ritenzione / peso_corporeo * 1000
    In riferimenti Excel:
      = (C{row}/100) * Esposizione!$B$12 * Esposizione!$B$13 / Esposizione!$B$5 * 1000
    """
    ws = wb.create_sheet("SED")

    ws.merge_cells('A1:F1')
    ws['A1'] = "CALCOLO SYSTEMIC EXPOSURE DOSAGE (SED)"
    ws['A1'].font = TITLE_FONT

    hdr = 3
    headers = [
        "Ingrediente",
        "CAS",
        "Percentuale (%)",
        "Systemic Exposure Dosage - SED (mg/kg bw/giorno)",
        "Restrizioni COSING (Annex)",
        "Altre Restrizioni",
    ]
    for col, h in enumerate(headers, 1):
        ws.cell(row=hdr, column=col, value=h)
    _style_header_row(ws, hdr, len(headers))

    for i, pi in enumerate(project.ingredients):
        r = hdr + 1 + i
        name = _get_ingredient_name(pi)

        ws.cell(row=r, column=1, value=name)
        ws.cell(row=r, column=2, value=pi.cas or "")
        ws.cell(row=r, column=3, value=pi.percentage)

        # SED formula solo per ingredienti non esclusi
        if not pi.skip_tox and pi.cas:
            ws.cell(row=r, column=4).value = (
                f"=(C{r}/100)*Esposizione!$B$12*Esposizione!$B$13"
                f"/Esposizione!$B$5*1000"
            )
            ws.cell(row=r, column=4).number_format = '0.000000'

        # Restrizioni COSING
        annex, other = _get_cosing_restrictions(pi.ingredient if not pi.skip_tox else None)
        ws.cell(row=r, column=5, value=annex)
        ws.cell(row=r, column=6, value=other)

        # Evidenzia riga se ha restrizioni
        if annex:
            for col in range(1, len(headers) + 1):
                ws.cell(row=r, column=col).fill = WARNING_FILL

        _apply_borders(ws, r, len(headers))

    _set_column_widths(ws, [18, 14, 14, 20, 18, 18])


def _build_mos(wb, project):
    """
    Sheet 4: Calcolo Margin of Safety.

    Formule Excel:
      SED (col C)     = (B{r}/100) * Esposizione!$B$12 * Esposizione!$B$13 / Esposizione!$B$5 * 1000
      SED con DAP (E) = C{r} * D{r}
      MoS (I)         = G{r} / (E{r} * H{r})   [se E > 0 e H > 0]
    """
    ws = wb.create_sheet("MoS")

    ws.merge_cells('A1:N1')
    ws['A1'] = "CALCOLO MARGIN OF SAFETY (MoS)"
    ws['A1'].font = TITLE_FONT

    hdr = 3
    headers = [
        "Nome Ingrediente",                                             # A
        "Percentuale (%)",                                              # B
        "Systemic Exposure Dosage - SED (mg/kg bw/giorno)",             # C
        "Dermal Absorption Percentage (DAP)",                           # D
        "SED corretto con DAP (mg/kg bw/giorno)",                       # E
        "Indicatore Tossicologico (NOAEL / LOAEL / LD50)",              # F
        "Valore Indicatore (mg/kg bw/giorno)",                          # G
        "Fattore di Sicurezza",                                         # H
        "Margin of Safety (MoS)",                                       # I
        "Fonte del Dato",                                               # J
        "Informazioni DAP (Peso Molecolare, LogP, TPSA, Punto Fusione)",# K
        "Restrizioni COSING (Annex)",                                   # L
        "Altre Restrizioni",                                            # M
        "Note",                                                         # N
    ]
    for col, h in enumerate(headers, 1):
        ws.cell(row=hdr, column=col, value=h)
    _style_header_row(ws, hdr, len(headers))

    num_cols = len(headers)
    r = hdr + 1

    for pi in project.ingredients:
        # Salta ingredienti esclusi (skip_tox o senza CAS)
        if pi.skip_tox or not pi.cas:
            continue

        name = _get_ingredient_name(pi)
        ing = pi.ingredient
        best = ing.toxicity.best_case if ing and ing.toxicity else None

        # A: Nome
        ws.cell(row=r, column=1, value=name)

        # B: Percentuale
        ws.cell(row=r, column=2, value=pi.percentage)

        # C: SED (senza DAP) - formula Excel
        ws.cell(row=r, column=3).value = (
            f"=(B{r}/100)*Esposizione!$B$12*Esposizione!$B$13"
            f"/Esposizione!$B$5*1000"
        )
        ws.cell(row=r, column=3).number_format = '0.000000'

        # D: DAP
        dap_value = ing.dap_info.dap_value if ing and ing.dap_info else 0.5
        ws.cell(row=r, column=4, value=dap_value)

        # E: SED con DAP - formula Excel
        ws.cell(row=r, column=5).value = f"=C{r}*D{r}"
        ws.cell(row=r, column=5).number_format = '0.000000'

        # F: Tipo indicatore
        ws.cell(row=r, column=6, value=best.indicator if best else "")

        # G: Valore indicatore
        ws.cell(row=r, column=7, value=best.value if best else "")

        # H: Fattore di sicurezza
        factor = best.factor if best else 1
        ws.cell(row=r, column=8, value=factor)

        # I: MoS - formula Excel
        ws.cell(row=r, column=9).value = (
            f'=IF(AND(E{r}>0,H{r}>0),G{r}/(E{r}*H{r}),"")'
        )
        ws.cell(row=r, column=9).number_format = '0.00'

        # J: Fonte
        ws.cell(row=r, column=10, value=best.ref if best else "")

        # K: Informazioni DAP
        ws.cell(row=r, column=11, value=_get_dap_info_text(ing))

        # L, M: Restrizioni
        annex, other = _get_cosing_restrictions(ing)
        ws.cell(row=r, column=12, value=annex)
        ws.cell(row=r, column=13, value=other)

        # N: Note (vuoto)
        ws.cell(row=r, column=14, value="")

        # Stile: bordi + evidenzia se MoS potrebbe essere basso
        _apply_borders(ws, r, num_cols)

        # Alterna colore righe per leggibilita
        if (r - hdr) % 2 == 0:
            for col in range(1, num_cols + 1):
                ws.cell(row=r, column=col).fill = LIGHT_FILL

        r += 1

    # Legenda sotto la tabella
    legend_row = r + 2
    ws.cell(row=legend_row, column=1, value="LEGENDA").font = LABEL_FONT
    ws.cell(row=legend_row + 1, column=1, value="Fattore di Sicurezza:")
    ws.cell(row=legend_row + 1, column=2, value="NOAEL = 1, LOAEL = 3, LD50 = 10")
    ws.cell(row=legend_row + 2, column=1, value="MoS accettabile:")
    ws.cell(row=legend_row + 2, column=2, value=">= 100 (secondo linee guida SCCS)")
    ws.cell(row=legend_row + 3, column=1, value="Formula MoS:")
    ws.cell(row=legend_row + 3, column=2, value="Valore Indicatore / (SED con DAP x Fattore di Sicurezza)")

    _set_column_widths(ws, [18, 10, 14, 8, 14, 12, 12, 10, 10, 18, 22, 18, 18, 14])


# ==================== MAIN EXPORT ====================

def export_project_excel(project, output_path: str = None) -> str:
    """
    Esporta un Project completo in un file Excel (.xlsx).

    Args:
        project: oggetto Project da esportare
        output_path: percorso del file di output (default: exports/progetto_{order_id}.xlsx)

    Returns:
        Il percorso del file Excel generato.
    """
    if output_path is None:
        os.makedirs("exports", exist_ok=True)
        output_path = f"exports/progetto_{project.order_id}.xlsx"
    else:
        dir_name = os.path.dirname(output_path)
        if dir_name:
            os.makedirs(dir_name, exist_ok=True)

    wb = Workbook()

    _build_anagrafica(wb, project)
    _build_esposizione(wb, project)
    _build_sed(wb, project)
    _build_mos(wb, project)

    wb.save(output_path)
    logger.info(f"Excel esportato: {output_path}")

    return output_path
