import json
import uuid
from typing import Any, Dict, List, Optional

from sqlalchemy import text

from app.db import engine
from app.config import settings
from app.opportunity_service import set_opportunity_stage
from app.workflow_guard import validate_operation_action

_SCHEMA_READY = False

OPERATION_ACTIONS: Dict[str, Dict[str, str]] = {
    "jasmin_quotation": {"label": "Registar orçamento Jasmin", "system": "jasmin", "external_type": "quotation", "status": "created", "stage": "QUOTE_SENT", "note": "Orçamento criado/registado no Jasmin."},
    "jasmin_proforma": {"label": "Registo legado de pró-forma", "system": "jasmin", "external_type": "proforma", "status": "issued", "stage": "QUOTE_SENT", "note": "Registo legado tratado como orçamento/pedido de pagamento."},
    "payment_confirmed": {"label": "Registar pagamento confirmado", "system": "clientflow", "external_type": "payment", "status": "confirmed", "stage": "PAYMENT_CONFIRMED", "note": "Pagamento confirmado pelo operador."},
    "odoo_sale_order": {"label": "Registar venda Odoo", "system": "odoo", "external_type": "sale_order", "status": "created", "stage": "ODOO_ORDER_CREATED", "note": "Venda/encomenda registada no Odoo."},
    "odoo_production": {"label": "Registar produção Odoo", "system": "odoo", "external_type": "production", "status": "in_progress", "stage": "IN_PRODUCTION", "note": "Produção/preparação em curso no Odoo."},
    "odoo_physical_validated": {"label": "Validar encomenda física", "system": "odoo", "external_type": "physical_validation", "status": "validated", "stage": "READY_TO_SHIP", "note": "Encomenda física validada no Odoo."},
    "jasmin_invoice": {"label": "Registar fatura Jasmin", "system": "jasmin", "external_type": "invoice", "status": "issued", "stage": "INVOICED", "note": "Fatura emitida/registada no Jasmin."},
    "packlink_shipment": {"label": "Registar envio Packlink", "system": "packlink", "external_type": "shipment", "status": "created", "stage": "SHIPMENT_CREATED", "note": "Envio criado/registado no Packlink."},
    "tracking_sent": {"label": "Registar tracking enviado", "system": "clientflow", "external_type": "tracking", "status": "sent", "stage": "TRACKING_SENT", "note": "Tracking enviado ao cliente via Chatwoot/email."},
    "delivered": {"label": "Registar entrega concluída", "system": "packlink", "external_type": "delivery", "status": "delivered", "stage": "WON", "note": "Processo entregue/concluído."},
}

OPERATION_CARDS: List[Dict[str, str]] = [
    {"key": "payment", "label": "Pagamento", "system": "clientflow", "external_type": "payment", "empty": "Por confirmar"},
    {"key": "odoo_sale_order", "label": "Venda Odoo", "system": "odoo", "external_type": "sale_order", "empty": "Não criada"},
    {"key": "odoo_physical_status", "label": "Estado físico Odoo", "system": "odoo", "external_type": "physical_status", "empty": "Não sincronizado"},
    {"key": "odoo_production", "label": "Produção Odoo", "system": "odoo", "external_type": "production", "empty": "Não iniciada"},
    {"key": "physical_validation", "label": "Encomenda física", "system": "odoo", "external_type": "physical_validation", "empty": "A validar"},
    {"key": "jasmin_quotation", "label": "Orçamento Jasmin", "system": "jasmin", "external_type": "quotation", "empty": "Não criado"},
    {"key": "jasmin_invoice", "label": "Fatura Jasmin", "system": "jasmin", "external_type": "invoice", "empty": "Não emitida"},
    {"key": "packlink_shipment", "label": "Envio Packlink", "system": "packlink", "external_type": "shipment", "empty": "Não criado"},
]

STATUS_LABELS = {
    "not_created": "Não criado", "pending": "Pendente", "issued": "Emitida", "confirmed": "Confirmado", "created": "Criado", "converted": "Convertido", "superseded": "Substituído", "in_progress": "Em curso", "validated": "Validado", "sent": "Enviado", "delivered": "Entregue", "failed": "Falhou", "ready_to_ship": "Pronta para despacho", "waiting_stock": "A aguardar stock", "in_production": "Em produção", "shipped": "Expedida", "order_created": "Venda criada", "quote_only": "Cotação", "not_found": "Não encontrada", "no_order": "Sem venda", "unknown": "Desconhecido", "cancelled": "Cancelada",
}

def _json(value: Any) -> str:
    return json.dumps(value or {}, ensure_ascii=False, default=str)

def ensure_operation_schema() -> None:
    global _SCHEMA_READY
    if _SCHEMA_READY:
        return
    with engine.begin() as conn:
        conn.execute(text("""
            CREATE TABLE IF NOT EXISTS operation_links (
                id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
                opportunity_id UUID NOT NULL REFERENCES opportunities(id) ON DELETE CASCADE,
                system TEXT NOT NULL,
                external_type TEXT NOT NULL,
                external_id TEXT,
                external_name TEXT,
                external_url TEXT,
                status TEXT NOT NULL DEFAULT 'pending',
                payload JSONB NOT NULL DEFAULT '{}'::jsonb,
                last_synced_at TIMESTAMPTZ,
                created_at TIMESTAMPTZ NOT NULL DEFAULT now(),
                updated_at TIMESTAMPTZ NOT NULL DEFAULT now()
            )
        """))
        conn.execute(text("""CREATE UNIQUE INDEX IF NOT EXISTS ux_operation_links_key ON operation_links(opportunity_id, system, external_type)"""))
        conn.execute(text("CREATE INDEX IF NOT EXISTS idx_operation_links_opportunity ON operation_links(opportunity_id)"))
        conn.execute(text("CREATE INDEX IF NOT EXISTS idx_operation_links_system ON operation_links(system, external_type, status)"))
    _SCHEMA_READY = True

def integration_settings_summary() -> Dict[str, Dict[str, Any]]:
    return {
        "odoo": {"enabled": bool(settings.odoo_enabled), "label": "Odoo", "public_url": settings.odoo_public_url or settings.odoo_base_url, "role": "Vendas, stock, produção e validação física"},
        "jasmin": {"enabled": bool(settings.jasmin_enabled), "label": "Jasmin", "public_url": settings.jasmin_public_url or settings.jasmin_base_url, "role": "Orçamentos, faturas e documentos fiscais"},
        "packlink": {"enabled": bool(settings.packlink_enabled), "label": "Packlink PRO", "public_url": settings.packlink_public_url or settings.packlink_base_url, "role": "Recolhas, envios, etiquetas e tracking"},
        "chatwoot": {"enabled": bool(settings.chatwoot_base_url), "label": "Chatwoot", "public_url": settings.chatwoot_public_url or settings.chatwoot_base_url, "role": "Comunicação com cliente"},
    }

def get_operation_links(opportunity_id: str) -> List[Dict[str, Any]]:
    ensure_operation_schema()
    with engine.begin() as conn:
        rows = conn.execute(text("""
            SELECT id::text, opportunity_id::text, system, external_type, external_id, external_name, external_url, status, payload, last_synced_at, created_at, updated_at
            FROM operation_links
            WHERE opportunity_id = CAST(:opportunity_id AS UUID)
            ORDER BY system, external_type
        """), {"opportunity_id": opportunity_id}).mappings().all()
    return [dict(r) for r in rows]

def get_operation_snapshot(opportunity_id: str) -> Dict[str, Any]:
    links = get_operation_links(opportunity_id)
    by_key = {(x["system"], x["external_type"]): x for x in links}
    cards = []
    for card in OPERATION_CARDS:
        link = by_key.get((card["system"], card["external_type"]))
        if link:
            status = link.get("status") or "pending"
            cards.append({**card, **link, "status_label": STATUS_LABELS.get(status, status)})
        else:
            cards.append({**card, "status": "not_created", "status_label": card["empty"], "external_name": "", "external_url": ""})
    return {"cards": cards, "links": links, "settings": integration_settings_summary(), "actions": OPERATION_ACTIONS}

def register_operation_action(opportunity_id: str, action_key: str, *, external_id: str = "", external_name: str = "", external_url: str = "", note: str = "", payload: Optional[Dict[str, Any]] = None, created_by: str = "operator") -> Dict[str, Any]:
    ensure_operation_schema()
    action = OPERATION_ACTIONS.get(str(action_key or ""))
    if not action:
        raise ValueError(f"Unsupported operation action: {action_key}")
    validate_operation_action(opportunity_id, action_key)
    system = action["system"]
    external_type = action["external_type"]
    status = action["status"]
    stage = action["stage"]
    note = note or action.get("note") or action.get("label") or action_key
    external_name = external_name or action.get("label") or external_type
    with engine.begin() as conn:
        row = conn.execute(text("""
            INSERT INTO operation_links (opportunity_id, system, external_type, external_id, external_name, external_url, status, payload, last_synced_at, updated_at)
            VALUES (CAST(:opportunity_id AS UUID), CAST(:system AS TEXT), CAST(:external_type AS TEXT), NULLIF(CAST(:external_id AS TEXT), ''), NULLIF(CAST(:external_name AS TEXT), ''), NULLIF(CAST(:external_url AS TEXT), ''), CAST(:status AS TEXT), CAST(:payload AS JSONB), now(), now())
            ON CONFLICT (opportunity_id, system, external_type)
            DO UPDATE SET external_id = COALESCE(EXCLUDED.external_id, operation_links.external_id), external_name = COALESCE(EXCLUDED.external_name, operation_links.external_name), external_url = COALESCE(EXCLUDED.external_url, operation_links.external_url), status = EXCLUDED.status, payload = operation_links.payload || EXCLUDED.payload, last_synced_at = now(), updated_at = now()
            RETURNING id::text, system, external_type, status
        """), {"opportunity_id": opportunity_id, "system": system, "external_type": external_type, "external_id": external_id or "", "external_name": external_name or "", "external_url": external_url or "", "status": status, "payload": _json({"action_key": action_key, "note": note, **(payload or {})})}).mappings().first()
        conn.execute(text("""
            INSERT INTO opportunity_events (id, opportunity_id, event_type, from_stage, to_stage, note, payload, created_by)
            SELECT CAST(:id AS UUID), id, 'operation_action_registered', stage, CAST(:stage AS TEXT), CAST(:note AS TEXT), CAST(:payload AS JSONB), CAST(:created_by AS TEXT)
            FROM opportunities WHERE id = CAST(:opportunity_id AS UUID)
        """), {"id": str(uuid.uuid4()), "opportunity_id": opportunity_id, "stage": stage, "note": note, "payload": _json({"action_key": action_key, "system": system, "external_type": external_type, "status": status}), "created_by": created_by})
    set_opportunity_stage(opportunity_id, stage, note=note, created_by=created_by)
    if str(action_key or "") == "payment_confirmed":
        try:
            from app.task_service import mark_obsolete_payment_followup_tasks

            closed = mark_obsolete_payment_followup_tasks(
                opportunity_id,
                actor=created_by or "operator",
                reason="Pagamento confirmado registado; fechar follow-ups/confirmações de pagamento pendentes.",
            )
            result = dict(row or {})
            result["obsolete_tasks_closed"] = closed
            return result
        except Exception as exc:
            print(f"ClientFlow obsolete payment task cleanup failed for {opportunity_id}: {exc}", flush=True)
    return dict(row or {})

def operation_next_steps(snapshot: Dict[str, Any], stage: str) -> List[str]:
    stage = str(stage or "")
    status_by = {c["key"]: c.get("status") for c in snapshot.get("cards", [])}
    steps: List[str] = []
    if stage in {"QUOTE_SENT", "WAITING_PAYMENT"} and status_by.get("payment") != "confirmed":
        steps.append("Confirmar pagamento com base no orçamento antes de emitir fatura e preparar envio.")
    if stage in {"PAYMENT_CONFIRMED"} and status_by.get("jasmin_invoice") == "not_created":
        steps.append("Emitir/registar fatura no Jasmin depois do pagamento confirmado.")
    if stage in {"PAYMENT_CONFIRMED", "INVOICED"} and status_by.get("odoo_sale_order") == "not_created":
        steps.append("Criar venda/encomenda no Odoo com os dados validados no ClientFlow.")
    if stage in {"ODOO_ORDER_CREATED", "IN_PRODUCTION"} and status_by.get("physical_validation") != "validated":
        steps.append("Aguardar produção/preparação e validar a encomenda física no Odoo.")
    if stage in {"READY_TO_SHIP", "INVOICED"} and status_by.get("jasmin_invoice") != "issued":
        steps.append("Emitir fatura no Jasmin antes do envio, salvo exceção operacional.")
    if stage in {"READY_TO_SHIP", "INVOICED"} and status_by.get("packlink_shipment") != "created":
        steps.append("Criar envio/recolha no Packlink quando a encomenda estiver pronta.")
    if stage == "SHIPMENT_CREATED" and status_by.get("tracking") != "sent":
        steps.append("Enviar tracking ao cliente pelo Chatwoot/email.")
    if not steps:
        steps.append("Fluxo sem bloqueios aparentes. Rever tarefas pendentes e próximos contactos.")
    return steps
