#!/usr/bin/env python3
"""Repair stale physical/shipment tasks when Odoo WH/OUT is already done.

Dry-run by default.  The repair is deliberately narrow: it requires explicit
Odoo delivery-done evidence for the selected opportunity before changing tasks
or the operational stage.
"""
from __future__ import annotations

import argparse
import json
import uuid
from typing import Any

from sqlalchemy import text

from app.db import engine
from app.odoo_delivery_task_reconciliation import reconcile_odoo_delivery_done


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


def _payload(value: Any) -> dict[str, Any]:
    if isinstance(value, dict):
        return value
    if isinstance(value, str) and value.strip():
        try:
            parsed = json.loads(value)
            return parsed if isinstance(parsed, dict) else {}
        except Exception:
            return {}
    return {}


def _done_evidence(status: str, payload: dict[str, Any]) -> bool:
    if str(status or "").lower() in {"shipped", "done", "delivered"}:
        return True
    if bool(payload.get("delivery_done")):
        return True
    pickings = payload.get("pickings") or payload.get("outgoing_pickings") or []
    outgoing = [p for p in pickings if isinstance(p, dict)]
    return bool(outgoing) and all(str(p.get("state") or "").lower() == "done" for p in outgoing)


def main() -> int:
    parser = argparse.ArgumentParser()
    parser.add_argument("--opportunity-id", required=True)
    parser.add_argument("--apply", action="store_true")
    args = parser.parse_args()

    with engine.begin() as conn:
        opportunity = conn.execute(text("""
            SELECT id::text, title, stage, status
            FROM opportunities
            WHERE id = CAST(:opportunity_id AS UUID)
            FOR UPDATE
        """), {"opportunity_id": args.opportunity_id}).mappings().first()
        if not opportunity:
            raise SystemExit("Oportunidade não encontrada.")

        physical = conn.execute(text("""
            SELECT status, payload
            FROM operation_links
            WHERE opportunity_id = CAST(:opportunity_id AS UUID)
              AND system = 'odoo'
              AND external_type = 'physical_status'
            LIMIT 1
        """), {"opportunity_id": args.opportunity_id}).mappings().first() or {}
        payload = _payload(physical.get("payload"))

        tasks = 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
        """), {"opportunity_id": args.opportunity_id}).mappings().all()

        print(f"Opportunity: {opportunity['title']} ({opportunity['stage']})")
        print(f"Odoo physical status: {physical.get('status')}")
        print(f"Delivery done evidence: {_done_evidence(str(physical.get('status') or ''), payload)}")
        print(f"Pending stale actions: {[row['action_code'] for row in tasks]}")

        if not _done_evidence(str(physical.get("status") or ""), payload):
            raise SystemExit("ABORTADO: não existe evidência inequívoca de WH/OUT concluído.")
        if not tasks and str(opportunity.get("stage") or "") == "SHIPPED":
            print("Sem alterações necessárias.")
            return 0
        if not args.apply:
            print("SAFE: concluir tasks físicas/expedição pendentes e alinhar fase para SHIPPED.")
            print("Dry-run only. Use --apply.")
            return 0

        result = reconcile_odoo_delivery_done(
            conn,
            args.opportunity_id,
            evidence=payload,
            actor="manual_v132_4_odoo_done_repair",
            upsert_validation=True,
        )
        old_stage = str(opportunity.get("stage") or "")
        conn.execute(text("""
            UPDATE opportunities
            SET stage = 'SHIPPED',
                status = 'open',
                last_action_code = 'CLOSE_OPPORTUNITY',
                metadata = COALESCE(metadata, '{}'::jsonb) || CAST(:metadata AS JSONB),
                updated_at = now()
            WHERE id = CAST(:opportunity_id AS UUID)
              AND status = 'open'
              AND stage IN (
                  'ODOO_ORDER_CREATED', 'IN_PRODUCTION', 'ORDER_PREPARATION',
                  'READY_TO_SHIP', 'SHIPMENT_CREATED', 'SHIPPED'
              )
        """), {
            "opportunity_id": args.opportunity_id,
            "metadata": _json({
                "odoo_done_stage_aligned": True,
                "odoo_done_stage_from": old_stage,
                "odoo_done_stage_to": "SHIPPED",
                "odoo_done_stage_version": "v4928.1.5.132.4",
            }),
        })
        conn.execute(text("""
            INSERT INTO opportunity_events (
                id, opportunity_id, event_type, action_code, from_stage, to_stage,
                note, payload, created_by
            ) VALUES (
                CAST(:id AS UUID), CAST(:opportunity_id AS UUID),
                'v132_4_odoo_done_repair', 'CLOSE_OPPORTUNITY',
                :from_stage, 'SHIPPED', :note, CAST(:payload AS JSONB), :created_by
            )
        """), {
            "id": str(uuid.uuid4()),
            "opportunity_id": args.opportunity_id,
            "from_stage": old_stage,
            "note": "WH/OUT já concluído no Odoo; fase e tasks operacionais alinhadas.",
            "payload": _json(result),
            "created_by": "manual_v132_4_odoo_done_repair",
        })

    print("Resultado:", result)
    print("Fase: SHIPPED")
    print("Próxima ação esperada: CLOSE_OPPORTUNITY")
    return 0


if __name__ == "__main__":
    raise SystemExit(main())
