"""Reconcile operator tasks when Odoo proves the outgoing delivery is done.

A completed WH/OUT is stronger evidence than pending ClientFlow tasks that ask
an operator to validate the physical order or create the shipment.  Those tasks
must stop dominating the workbench once the external evidence is unequivocal.
"""
from __future__ import annotations

import json
import uuid
from typing import Any, Mapping

from sqlalchemy import text


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


def _picking_names(evidence: Mapping[str, Any] | None) -> list[str]:
    result: list[str] = []
    for picking in (evidence or {}).get("pickings", []) or (evidence or {}).get("outgoing_pickings", []) or []:
        if not isinstance(picking, Mapping):
            continue
        if str(picking.get("state") or "").strip().lower() != "done":
            continue
        name = str(picking.get("name") or "").strip()
        if name and name not in result:
            result.append(name)
    return result


def reconcile_odoo_delivery_done(
    conn: Any,
    opportunity_id: str,
    *,
    evidence: Mapping[str, Any] | None = None,
    actor: str = "odoo_sync",
    upsert_validation: bool = True,
) -> dict[str, Any]:
    """Persist Odoo-done evidence and auto-complete superseded pending tasks.

    This deliberately does not call the normal task completion cascade: the
    Odoo delivery is already beyond both VALIDATE_PHYSICAL_ORDER and
    CREATE_SHIPMENT, so replaying those cascades would create fresh stale work.
    """

    evidence = dict(evidence or {})
    picking_names = _picking_names(evidence)
    sale = evidence.get("sale_order") if isinstance(evidence.get("sale_order"), Mapping) else {}
    external_id = str((sale or {}).get("id") or evidence.get("sale_order_id") or "").strip() or None
    external_name = str((sale or {}).get("name") or evidence.get("sale_order") or "").strip() or None

    if upsert_validation:
        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), 'odoo', 'physical_validation',
                :external_id, :external_name, NULL, 'validated', 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),
                status = 'validated',
                payload = operation_links.payload || EXCLUDED.payload,
                last_synced_at = now(),
                updated_at = now()
        """), {
            "opportunity_id": opportunity_id,
            "external_id": external_id,
            "external_name": external_name or "WH/OUT concluído no Odoo",
            "payload": _json({
                **evidence,
                "validated_from_odoo_delivery_done": True,
                "validated_by": actor,
                "version": "v4928.1.5.132.4",
            }),
        })

    rows = conn.execute(text("""
        SELECT id::text, action_code, status
        FROM tasks
        WHERE opportunity_id = CAST(:opportunity_id AS UUID)
          AND status = 'pending'
          AND action_code IN ('VALIDATE_PHYSICAL_ORDER', 'CREATE_SHIPMENT')
        ORDER BY created_at
        FOR UPDATE
    """), {"opportunity_id": opportunity_id}).mappings().all()

    completed: list[dict[str, str]] = []
    for row in rows:
        task_id = str(row.get("id") or "")
        action_code = str(row.get("action_code") or "")
        if not task_id:
            continue
        reason = (
            "Odoo WH/OUT concluído confirma a preparação física e expedição."
            if action_code == "VALIDATE_PHYSICAL_ORDER"
            else "Odoo WH/OUT concluído confirma que a expedição já foi executada."
        )
        metadata = {
            "auto_completed_from_odoo_delivery_done": True,
            "auto_completed_by": actor,
            "auto_completed_version": "v4928.1.5.132.4",
            "auto_completed_reason": "odoo_delivery_done_supersedes_pending_action",
            "odoo_done_pickings": picking_names,
            "previous_status": str(row.get("status") or "pending"),
        }
        updated = conn.execute(text("""
            UPDATE tasks
            SET status = 'done',
                action_required = FALSE,
                done_at = COALESCE(done_at, now()),
                done_by = COALESCE(done_by, :actor),
                note = CASE
                    WHEN COALESCE(note, '') = '' THEN :reason
                    ELSE note || E'\n\n' || :reason
                END,
                metadata = COALESCE(metadata, '{}'::jsonb) || CAST(:metadata AS JSONB),
                updated_at = now()
            WHERE id = CAST(:task_id AS UUID)
              AND status = 'pending'
            RETURNING id::text
        """), {
            "task_id": task_id,
            "actor": actor,
            "reason": reason,
            "metadata": _json(metadata),
        }).scalar()
        if not updated:
            continue
        conn.execute(text("""
            INSERT INTO task_events (task_id, event_type, payload, created_by)
            VALUES (
                CAST(:task_id AS UUID), 'task_auto_completed',
                CAST(:payload AS JSONB), :actor
            )
        """), {
            "task_id": task_id,
            "actor": actor,
            "payload": _json({
                "action_code": action_code,
                "reason": "odoo_delivery_done_supersedes_pending_action",
                "odoo_done_pickings": picking_names,
                "version": "v4928.1.5.132.4",
            }),
        })
        completed.append({"task_id": task_id, "action_code": action_code})

    if completed:
        conn.execute(text("""
            INSERT INTO opportunity_events (
                id, opportunity_id, event_type, action_code, from_stage, to_stage,
                note, payload, created_by
            )
            SELECT
                CAST(:id AS UUID), id, 'odoo_delivery_done_tasks_reconciled',
                'CLOSE_OPPORTUNITY', stage, stage, :note, CAST(:payload AS JSONB), :actor
            FROM opportunities
            WHERE id = CAST(:opportunity_id AS UUID)
        """), {
            "id": str(uuid.uuid4()),
            "opportunity_id": opportunity_id,
            "actor": actor,
            "note": "WH/OUT concluído no Odoo; tasks físicas/expedição obsoletas concluídas automaticamente.",
            "payload": _json({"completed_tasks": completed, "odoo_done_pickings": picking_names, "version": "v4928.1.5.132.4"}),
        })

    return {
        "physical_validation": "validated" if upsert_validation else "preserved",
        "completed_tasks": completed,
        "odoo_done_pickings": picking_names,
    }
