"""Central decision service for opportunity next actions.

v4928.1.5.60 delegates operational flow to app.domain.opportunity_flow so
opportunity pages, tasks and future audits consume the same decision vocabulary.
"""
from __future__ import annotations

from dataclasses import asdict, dataclass
from typing import Any, Dict, Optional

from sqlalchemy import text

from app.db import engine
# Backward-compatible static anchors from v1.5.59: quotation_doc, invoice_doc, confirmar pagamento antes de emitir fatura, Pagamento confirmado com base em, Criar/enviar fatura.
from app.domain.opportunity_flow import (
    OpportunityEvidence,
    build_opportunity_evidence,
    decide_opportunity_next_action,
    load_company_profile,
)


@dataclass
class OpportunityNextAction:
    action_code: str
    label: str
    description: str
    priority: str = "normal"
    target_url: Optional[str] = None
    can_execute: bool = True
    reason_if_blocked: Optional[str] = None
    document_id: Optional[str] = None
    document_number: Optional[str] = None

    def to_dict(self) -> Dict[str, Any]:
        return asdict(self)


def _first_row(conn: Any, sql: str, params: Dict[str, Any]) -> Optional[Dict[str, Any]]:
    row = conn.execute(text(sql), params).mappings().first()
    return dict(row) if row else None


def _rows(conn: Any, sql: str, params: Dict[str, Any]) -> list[Dict[str, Any]]:
    return [dict(r) for r in conn.execute(text(sql), params).mappings().all()]


def _operation_snapshot_safe(opportunity_id: str) -> dict[str, Any]:
    try:
        from app.operation_service import get_operation_snapshot

        return get_operation_snapshot(opportunity_id)
    except Exception:
        return {"cards": [], "links": []}


def _build_db_evidence(opportunity_id: str) -> OpportunityEvidence | None:
    params = {"opportunity_id": opportunity_id}
    with engine.begin() as conn:
        opp = _first_row(conn, """
            SELECT
                id::text,
                stage,
                status,
                title,
                local_customer_id::text AS fiscal_customer_id,
                local_customer_id::text AS customer_id,
                metadata
            FROM opportunities
            WHERE id = CAST(:opportunity_id AS UUID)
        """, params)
        if not opp:
            return None

        tasks = _rows(conn, """
            SELECT id::text, action_code, action, note, priority, route, status, due_at, created_at, metadata
            FROM tasks
            WHERE opportunity_id = CAST(:opportunity_id AS UUID)
            ORDER BY
              CASE COALESCE(priority, 'normal') WHEN 'alta' THEN 1 WHEN 'normal' THEN 2 WHEN 'baixa' THEN 3 ELSE 4 END,
              due_at NULLS LAST,
              created_at DESC
            LIMIT 20
        """, params)

        docs = _rows(conn, """
            SELECT id::text, external_id, document_kind, document_number, status, total_amount, document_date, role, is_active, is_primary, payload
            FROM commercial_documents
            WHERE opportunity_id = CAST(:opportunity_id AS UUID)
              AND system = 'jasmin'
              AND COALESCE(is_active, TRUE) = TRUE
              AND COALESCE(role, 'current') IN ('current', 'accepted', 'historical', 'history')
            ORDER BY
              CASE document_kind WHEN 'invoice' THEN 1 WHEN 'quotation' THEN 2 WHEN 'proforma' THEN 3 ELSE 4 END,
              CASE COALESCE(role, 'current') WHEN 'current' THEN 1 WHEN 'accepted' THEN 2 ELSE 3 END,
              COALESCE(document_date, created_at::date) DESC,
              created_at DESC
        """, params)

        linked_customer = None
        customer_id = opp.get("fiscal_customer_id") or opp.get("customer_id")
        if customer_id:
            linked_customer = _first_row(conn, """
                SELECT
                    id::text,
                    name,
                    tax_id,
                    email,
                    email AS billing_email,
                    street_name AS address,
                    postal_zone AS postal_code,
                    city_name AS city,
                    phone
                FROM customers
                WHERE id = CAST(:customer_id AS UUID)
            """, {"customer_id": customer_id})

        candidate = _first_row(conn, """
            SELECT id::text, source_system, external_type, document_number, title, confidence
            FROM reconciliation_items
            WHERE opportunity_id = CAST(:opportunity_id AS UUID)
              AND status = 'open'
            ORDER BY confidence DESC NULLS LAST, created_at DESC
            LIMIT 1
        """, params)

    snapshot = _operation_snapshot_safe(opportunity_id)
    fiscal_complete = False
    if linked_customer:
        fiscal_complete = bool(
            linked_customer.get("tax_id")
            and (linked_customer.get("billing_email") or linked_customer.get("email"))
            and linked_customer.get("address")
            and linked_customer.get("postal_code")
            and linked_customer.get("city")
        )

    return build_opportunity_evidence(
        opp,
        linked_documents=docs,
        tasks=tasks,
        operation_snapshot=snapshot,
        linked_customer=linked_customer,
        fiscal_data_complete=fiscal_complete,
        has_reconciliation_candidate=bool(candidate),
        reconciliation_label=(candidate or {}).get("document_number") or (candidate or {}).get("title"),
        company_profile="blif",
    )


def get_opportunity_next_action(opportunity_id: str) -> Dict[str, Any]:
    """Return the recommended operator action for one opportunity.

    This remains a read-only service and returns the legacy dict shape, but the
    decision is now produced by the company workflow engine.
    """

    evidence = _build_db_evidence(opportunity_id)
    if evidence is None:
        return OpportunityNextAction(
            action_code="NOT_FOUND",
            label="Oportunidade não encontrada",
            description="Não foi possível encontrar esta oportunidade.",
            priority="baixa",
            can_execute=False,
            reason_if_blocked="opportunity_not_found",
        ).to_dict()

    decision = decide_opportunity_next_action(evidence, load_company_profile(evidence.company_profile))
    return decision.to_dict()
