#!/usr/bin/env python3
import argparse
import datetime as dt
import json
import os
import re
import sys
from pathlib import Path

import requests


DELETE_ORDER = [
    "pagos",
    "pedido_items",
    "corte_items",
    "corte_jobs",
    "crm_oportunidades",
    "chatwoot_conversations",
    "presupuesto_items",
    "presupuestos",
    "pedidos",
    "movimientos",
    "proveedores",
    "clientes",
]

ACCOUNT_ALIASES = {
    "Macro": "Banco Macro",
    "BNA": "Banco Nación",
    "Banco Nacion": "Banco Nación",
}


def normalize_account(name):
    return ACCOUNT_ALIASES.get((name or "").strip(), (name or "Efectivo").strip())


def auth(base, email, password):
    base = base.rstrip("/")
    response = requests.post(
        base + "/api/collections/_superusers/auth-with-password",
        json={"identity": email, "password": password},
        timeout=30,
    )
    response.raise_for_status()
    return base, {"Authorization": "Bearer " + response.json()["token"]}


def all_records(base, headers, collection):
    items = []
    page = 1
    while True:
        response = requests.get(
            base + f"/api/collections/{collection}/records",
            headers=headers,
            params={"page": page, "perPage": 200},
            timeout=30,
        )
        response.raise_for_status()
        data = response.json()
        items.extend(data.get("items", []))
        if page >= data.get("totalPages", 1):
            break
        page += 1
    return items


def create_record(base, headers, collection, payload):
    response = requests.post(
        base + f"/api/collections/{collection}/records",
        headers=headers,
        json=payload,
        timeout=30,
    )
    if response.status_code >= 300:
        raise RuntimeError(f"create {collection}: {response.status_code} {response.text[:500]} payload={payload}")
    return response.json()


def delete_record(base, headers, collection, record_id):
    response = requests.delete(
        base + f"/api/collections/{collection}/records/{record_id}",
        headers=headers,
        timeout=30,
    )
    if response.status_code >= 300 and response.status_code != 404:
        raise RuntimeError(f"delete {collection}/{record_id}: {response.status_code} {response.text[:500]}")


def parse_date(value):
    if not value:
        return ""
    text = str(value).replace("T", " ").replace("Z", "").split(".")[0]
    return text


def pb_datetime(value):
    text = parse_date(value)
    if not text:
        return ""
    if len(text) == 10:
        text += " 12:00:00"
    return f"{text}.000Z"


def date_only(value):
    text = parse_date(value)
    return text[:10] if text else ""


def order_number(reference):
    match = re.search(r"(\d+)$", str(reference or ""))
    return int(match.group(1)) if match else None


def order_suffix(reference):
    number = order_number(reference)
    return str(number) if number is not None else ""


def map_stage(stage, saldo):
    stage = (stage or "").strip().lower()
    saldo = float(saldo or 0)
    if "por medir" in stage:
        return "para_medir"
    if "para cortar" in stage:
        return "para_cortar"
    if "armando" in stage:
        return "cortando"
    if "para armar" in stage:
        return "cortado"
    if "terminado" in stage:
        return "terminado"
    if "para instalar" in stage:
        return "para_instalar"
    if "entregado" in stage or "finalizado" in stage:
        return "entregado_con_saldo" if saldo > 0 else "entregado_sin_saldo"
    return "pedido_nuevo"


def map_order_status(etapa):
    if etapa in {"entregado_con_saldo", "entregado_sin_saldo"}:
        return "entregado"
    if etapa == "terminado":
        return "terminado"
    if etapa in {"para_cortar", "cortando", "cortado", "para_instalar"}:
        return "en_produccion"
    return "cargado"


def map_cut_status(etapa):
    if etapa in {"cortando"}:
        return "en_corte"
    if etapa in {"cortado", "terminado", "para_instalar", "entregado_con_saldo", "entregado_sin_saldo"}:
        return "cortados"
    return "pendiente"


def ingreso_category(detalle):
    text = (detalle or "").lower()
    if "seña" in text:
        return "Seña"
    if "saldo" in text:
        return "Saldo final"
    if "total" in text:
        return "Venta directa"
    if "ent." in text:
        return "Pago parcial"
    return "Otros"


def egreso_category(detalle):
    text = (detalle or "").lower()
    if "edd" in text:
        return "Cuenta socio"
    if any(word in text for word in ["anibal", "ciarrocchi", "vaquero", "flete"]):
        return "Mano de obra"
    if "edes" in text or "software" in text:
        return "Servicios"
    if "alquiler" in text:
        return "Alquiler / gastos del local"
    return "Otros"


def movement_category(movement):
    if movement.get("tipo") == "ingreso":
        return ingreso_category(movement.get("detalle"))
    if movement.get("tipo") == "egreso":
        return egreso_category(movement.get("detalle"))
    return ""


def dry_summary(orders, old_movements):
    total = sum(float(row.get("Total") or 0) for row in orders)
    saldo = sum(float(row.get("Saldo") or 0) for row in orders)
    return {
        "odoo_orders": len(orders),
        "old_caja_movements": len(old_movements),
        "odoo_total": total,
        "odoo_saldo": saldo,
        "odoo_cobrado": total - saldo,
    }


def main():
    parser = argparse.ArgumentParser()
    parser.add_argument("--orders-json", required=True)
    parser.add_argument("--apply", action="store_true")
    args = parser.parse_args()

    new_base, new_headers = auth(os.environ["NEW_PB_URL"], os.environ["NEW_EMAIL"], os.environ["NEW_PASS"])
    old_base, old_headers = auth(os.environ["OLD_PB_URL"], os.environ["OLD_EMAIL"], os.environ["OLD_PASS"])

    orders = json.loads(Path(args.orders_json).read_text(encoding="utf-8"))
    old_movements = all_records(old_base, old_headers, "movimientos")
    print(json.dumps(dry_summary(orders, old_movements), ensure_ascii=False, indent=2))

    if not args.apply:
        print("DRY RUN: add --apply to delete current test data and import.")
        return

    before = {collection: len(all_records(new_base, new_headers, collection)) for collection in DELETE_ORDER}
    print("before_delete", json.dumps(before, ensure_ascii=False))

    for collection in DELETE_ORDER:
        for record in all_records(new_base, new_headers, collection):
            delete_record(new_base, new_headers, collection, record["id"])
        print(f"deleted {collection}")

    clientes_by_key = {}
    pedidos_by_suffix = {}
    created_clientes = 0
    created_pedidos = 0

    for row in orders:
        name = str(row.get("Cliente") or "").strip() or "Sin nombre"
        phone = str(row.get("Teléfono") or "").strip()
        phone_digits = re.sub(r"\D", "", phone)
        client_key = phone_digits or name.lower()
        cliente = clientes_by_key.get(client_key)
        if not cliente:
            cliente = create_record(new_base, new_headers, "clientes", {
                "nombre": name,
                "celular": phone,
                "direccion": "",
                "notas": "Importado desde Odoo.",
            })
            clientes_by_key[client_key] = cliente
            created_clientes += 1

        reference = str(row.get("Referencia de la orden") or "").strip()
        total = float(row.get("Total") or 0)
        saldo = float(row.get("Saldo") or 0)
        paid = max(0, total - saldo)
        etapa = map_stage(row.get("Etapa"), saldo)
        pedido = create_record(new_base, new_headers, "pedidos", {
            "codigo": reference,
            "numero": order_number(reference),
            "referencia_odoo": reference,
            "cliente": cliente["id"],
            "fecha_creacion": pb_datetime(row.get("Fecha de la orden")),
            "fecha_entrega_estimada": pb_datetime(row.get("Fecha esperada")),
            "total": total,
            "pagado": paid,
            "senia": paid,
            "saldo": saldo,
            "estado_pedido": map_order_status(etapa),
            "estado_cortes": map_cut_status(etapa),
            "etapa_produccion": etapa,
            "avisado_retiro": False,
            "hay_que_medir": etapa == "para_medir",
            "hay_que_enviar": etapa == "para_instalar",
            "descripcion": (
                f"Importado desde Odoo. Referencia: {reference}.\n"
                f"Etapa Odoo: {row.get('Etapa') or 'sin etapa'}.\n"
                f"Cuaderno: {'sí' if row.get('Cuaderno') else 'no'}."
            ),
        })
        suffix = order_suffix(reference)
        if suffix:
            pedidos_by_suffix[suffix] = pedido
        created_pedidos += 1

    created_movements = 0
    for movement in old_movements:
        payload = {
            "tipo": movement.get("tipo") or "ingreso",
            "monto": float(movement.get("monto") or 0),
            "cuenta": normalize_account(movement.get("cuenta")),
            "cuenta_destino": "",
            "detalle": movement.get("detalle") or "",
            "categoria": movement_category(movement),
            "notas": f"Importado desde Caja vieja. ID origen: {movement.get('id')}",
            "numero_pedido": str(movement.get("numero_pedido") or "").lstrip("0"),
            "fecha": date_only(movement.get("fecha")) or dt.date.today().isoformat(),
            "check_odoo": bool(movement.get("check_odoo")),
            "check_contabilidad": bool(movement.get("check_contabilidad")),
        }
        create_record(new_base, new_headers, "movimientos", payload)
        created_movements += 1

    after = {
        "clientes": len(all_records(new_base, new_headers, "clientes")),
        "pedidos": len(all_records(new_base, new_headers, "pedidos")),
        "movimientos": len(all_records(new_base, new_headers, "movimientos")),
        "pagos": len(all_records(new_base, new_headers, "pagos")),
    }
    print("imported", json.dumps({
        "clientes": created_clientes,
        "pedidos": created_pedidos,
        "movimientos": created_movements,
    }, ensure_ascii=False))
    print("after", json.dumps(after, ensure_ascii=False))


if __name__ == "__main__":
    try:
        main()
    except Exception as exc:
        print(f"FATAL: {exc}", file=sys.stderr)
        sys.exit(1)
