"""Business-friendly output formatter for NLQ results."""

from __future__ import annotations

import csv
import json
from pathlib import Path
from typing import Any

from cli_anything.odoo_12.core.nlq import ParsedQuery
from cli_anything.odoo_12.core.cross_group import CrossGroupResult


# ── Field Labels (technical → human-readable) ────────────────

_FIELD_LABELS: dict[str, str] = {
    "id": "ID",
    "name": "Nome",
    "display_name": "Nome",
    "partner_id": "Parceiro",
    "date_invoice": "Data",
    "date_order": "Data",
    "payment_date": "Data Pgto",
    "create_date": "Criado em",
    "write_date": "Atualizado",
    "amount_total": "Total (R$)",
    "amount": "Valor (R$)",
    "list_price": "Preço",
    "qty_available": "Estoque",
    "state": "Status",
    "type": "Tipo",
    "number": "Número",
    "default_code": "Ref",
    "email": "E-mail",
    "phone": "Telefone",
    "login": "Login",
    "is_company": "Empresa?",
    "customer": "Cliente?",
    "supplier": "Fornecedor?",
    "active": "Ativo?",
    "vat": "CNPJ/CPF",
    "product_id": "Produto",
    "config_id": "Ponto de Venda",
    "session_id": "Sessão",
    "pos_reference": "Ref. Recibo",
}


def _field_label(field_name: str) -> str:
    """Translate a field technical name to a human-readable label."""
    return _FIELD_LABELS.get(field_name, field_name.replace("_", " ").title())


def _format_brl(value: Any) -> str:
    """Format a number as BRL currency string."""
    if value is None or value is False:
        return "—"
    try:
        num = float(value)
        # Format with dots as thousands sep, comma as decimal
        formatted = f"{num:,.2f}"
        # Swap: 1,234.56 → 1.234,56
        formatted = formatted.replace(",", "X").replace(".", ",").replace("X", ".")
        return formatted
    except (ValueError, TypeError):
        return str(value)


def _format_cell(value: Any) -> str:
    """Format a cell value for display."""
    if value is None or value is False:
        return "—"
    if isinstance(value, (list, tuple)) and len(value) == 2:
        return str(value[1])  # Many2one: (id, name) → name
    if isinstance(value, bool):
        return "Sim" if value else "Não"
    return str(value)


def _is_amount_field(field_name: str) -> bool:
    """Check if a field is a monetary amount."""
    return field_name in ("amount_total", "amount", "list_price", "price_total",
                          "price_subtotal", "residual", "amount_untaxed")


def format_business_output(
    skin: Any,
    parsed: ParsedQuery,
    records: list[dict],
    json_mode: bool = False,
) -> dict:
    """Format records as business-friendly output.

    Returns a dict with:
    - model, count, records (for JSON mode)
    - Prints formatted table (for plain mode)
    """
    count = len(records)
    result = {
        "model": parsed.model,
        "count": count,
        "domain": parsed.domain,
        "title": parsed.title,
        "records": records,
    }

    if json_mode:
        if parsed.group_by:
            result["groups"] = _build_groups(parsed, records)
        return result

    # Header
    skin.section(parsed.title)
    skin.info(f"Consulta: {parsed.model} | {count} registro(s)")

    if not records:
        skin.hint("Nenhum registro encontrado.")
        return result

    # Grouped output
    if parsed.group_by:
        _print_grouped(skin, parsed, records)
    else:
        _print_table(skin, parsed, records)

    # Export
    if parsed.export:
        _do_export(parsed, records)
        skin.success(f"Exportado para {parsed.export.path}")
        result["exported"] = parsed.export.path

    return result


def _print_table(skin: Any, parsed: ParsedQuery, records: list[dict]) -> None:
    """Print records as a flat table with footer."""
    display_fields = [f for f in parsed.fields if f in records[0]]
    if "id" not in display_fields and "id" in records[0]:
        display_fields.insert(0, "id")

    headers = [_field_label(f) for f in display_fields]
    rows = []
    total_sum = 0.0

    for rec in records:
        row = []
        for f in display_fields:
            val = rec.get(f)
            if _is_amount_field(f):
                row.append(_format_brl(val))
                if val and parsed.sum_field == f:
                    try:
                        total_sum += float(val)
                    except (ValueError, TypeError):
                        pass
            else:
                row.append(_format_cell(val))
        rows.append(row)

    skin.table(headers, rows)

    footer_parts = [f"Total: {len(records)} registro(s)"]
    if parsed.sum_field and total_sum > 0:
        footer_parts.append(f"Soma: R$ {_format_brl(total_sum)}")
    skin.info(" | ".join(footer_parts))


def _build_groups(parsed: ParsedQuery, records: list[dict]) -> list[dict]:
    """Build group summaries from records."""
    from collections import OrderedDict
    groups: dict[str, dict] = OrderedDict()

    for rec in records:
        val = rec.get(parsed.group_by)
        group_name = _format_cell(val) if val else "Sem Categoria"
        if group_name not in groups:
            groups[group_name] = {"name": group_name, "count": 0, "total": 0.0, "records": []}
        groups[group_name]["count"] += 1
        groups[group_name]["records"].append(rec)
        if parsed.sum_field:
            try:
                groups[group_name]["total"] += float(rec.get(parsed.sum_field, 0) or 0)
            except (ValueError, TypeError):
                pass

    return sorted(groups.values(), key=lambda g: -g["total"])


def _print_grouped(skin: Any, parsed: ParsedQuery, records: list[dict]) -> None:
    """Print records grouped by a field with subtotals."""
    groups = _build_groups(parsed, records)
    grand_total = 0.0
    grand_count = 0

    # Summary table
    summary_headers = ["Grupo", "Qtd", "Total (R$)", "%"]
    summary_rows = []
    total_amount = sum(g["total"] for g in groups)

    for g in groups:
        pct = (g["total"] / total_amount * 100) if total_amount > 0 else 0
        summary_rows.append([
            g["name"],
            str(g["count"]),
            _format_brl(g["total"]),
            f"{pct:.1f}%",
        ])
        grand_total += g["total"]
        grand_count += g["count"]

    skin.table(summary_headers, summary_rows)

    footer_parts = [f"Total: {grand_count} registro(s)"]
    if parsed.sum_field and grand_total > 0:
        footer_parts.append(f"Soma: R$ {_format_brl(grand_total)}")
    skin.info(" | ".join(footer_parts))


def format_cross_group_output(
    skin: Any,
    parsed: ParsedQuery,
    cg_result: CrossGroupResult,
    json_mode: bool = False,
) -> dict:
    """Format cross-model grouping results."""
    result = {
        "model": parsed.model,
        "title": parsed.title,
        "domain": parsed.domain,
        "group_by": cg_result.label,
        "groups": [
            {
                "name": g["name"],
                "count": g["count"],
                "qty": g["qty"],
                "total": g["total"],
                "pct": round(g["pct"], 1),
            }
            for g in cg_result.groups
        ],
        "grand_count": cg_result.grand_count,
        "grand_qty": cg_result.grand_qty,
        "grand_total": cg_result.grand_total,
    }

    if json_mode:
        return result

    # Header
    skin.section(f"{parsed.title} — por {cg_result.label}")
    skin.info(f"Consulta: {parsed.model} | {cg_result.grand_count} itens de linha")

    if not cg_result.groups:
        skin.hint("Nenhum registro encontrado.")
        return result

    # Summary table
    headers = [cg_result.label, "Itens", "Qtd", "Total (R$)", "%"]
    rows = []
    for g in cg_result.groups:
        rows.append([
            g["name"],
            str(g["count"]),
            f"{g['qty']:.0f}",
            _format_brl(g["total"]),
            f"{g['pct']:.1f}%",
        ])

    skin.table(headers, rows)

    # Footer
    footer_parts = [f"Total: {cg_result.grand_count} itens"]
    footer_parts.append(f"Qtd: {cg_result.grand_qty:.0f}")
    footer_parts.append(f"Soma: R$ {_format_brl(cg_result.grand_total)}")
    skin.info(" | ".join(footer_parts))

    # Export
    if parsed.export:
        _do_export_cross_group(parsed, cg_result)
        skin.success(f"Exportado para {parsed.export.path}")
        result["exported"] = parsed.export.path

    return result


def _do_export_cross_group(parsed: ParsedQuery, cg_result: CrossGroupResult) -> None:
    """Export cross-group results to CSV or JSON."""
    path = Path(parsed.export.path)
    path.parent.mkdir(parents=True, exist_ok=True)

    if parsed.export.format == "csv":
        fields = [cg_result.label, "Itens", "Qtd", "Total", "%"]
        with open(path, "w", newline="", encoding="utf-8") as f:
            writer = csv.writer(f)
            writer.writerow(fields)
            for g in cg_result.groups:
                writer.writerow([
                    g["name"], g["count"], f"{g['qty']:.0f}",
                    f"{g['total']:.2f}", f"{g['pct']:.1f}%",
                ])
    else:
        data = {
            "model": parsed.model,
            "title": parsed.title,
            "group_by": cg_result.label,
            "groups": cg_result.groups,
            "grand_total": cg_result.grand_total,
        }
        with open(path, "w", encoding="utf-8") as f:
            json.dump(data, f, indent=2, default=str, ensure_ascii=False)


def _do_export(parsed: ParsedQuery, records: list[dict]) -> None:
    """Execute export to CSV or JSON."""
    path = Path(parsed.export.path)
    path.parent.mkdir(parents=True, exist_ok=True)

    if parsed.export.format == "csv":
        fields = parsed.fields
        with open(path, "w", newline="", encoding="utf-8") as f:
            writer = csv.DictWriter(f, fieldnames=fields, extrasaction="ignore")
            writer.writeheader()
            for rec in records:
                row = {}
                for field in fields:
                    val = rec.get(field, "")
                    if isinstance(val, (list, tuple)) and len(val) == 2:
                        val = val[1]
                    elif isinstance(val, bool):
                        val = str(val)
                    row[field] = val
                writer.writerow(row)
    else:  # json
        data = {
            "model": parsed.model,
            "title": parsed.title,
            "record_count": len(records),
            "records": records,
        }
        with open(path, "w", encoding="utf-8") as f:
            json.dump(data, f, indent=2, default=str, ensure_ascii=False)
