#!/usr/bin/env python3
"""Close pending follow-up tasks on terminal opportunities.

Read-only by default. With --apply, marks pending FOLLOW_UP_* tasks as skipped
when the linked opportunity is already closed/concluded/lost/no-interest.
"""
from __future__ import annotations

import argparse
import json
from typing import Any

from sqlalchemy import text

from app.db import engine

FOLLOW_UP_ACTIONS = (
    "FOLLOW_UP_QUOTE",
    "FOLLOW_UP_PROFORMA",
    "FOLLOW_UP_PAYMENT",
    "FOLLOW_UP_CUSTOMER_REVIEW",
    "FOLLOW_UP_GENERIC",
)

TERMINAL_STAGES = ("WON", "LOST", "NO_INTEREST", "DELIVERED")


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


def find_candidates(limit: int) -> list[dict[str, Any]]:
    with engine.begin() as conn:
        rows = conn.execute(text("""
            SELECT
                t.id::text AS task_id,
                t.created_at,
                t.due_at,
                t.route,
                t.action_code,
                t.action,
                t.note,
                t.status AS task_status,
                t.priority,
                t.opportunity_id::text,
                o.title AS opportunity_title,
                o.customer_name,
                o.stage AS opportunity_stage,
                o.status AS opportunity_status,
                o.closed_at
            FROM tasks t
            JOIN opportunities o ON o.id = t.opportunity_id
            WHERE t.status = 'pending'
              AND t.action_code IN (
                'FOLLOW_UP_QUOTE',
                'FOLLOW_UP_PROFORMA',
                'FOLLOW_UP_PAYMENT',
                'FOLLOW_UP_CUSTOMER_REVIEW',
                'FOLLOW_UP_GENERIC'
              )
              AND (
                o.status = 'closed'
                OR o.stage IN ('WON', 'LOST', 'NO_INTEREST', 'DELIVERED')
              )
            ORDER BY t.due_at NULLS LAST, t.created_at DESC
            LIMIT :limit
        """), {"limit": int(limit)}).mappings().all()
    return [dict(row) for row in rows]


def apply_candidate(task_id: str, *, actor: str = "sync_closed_opportunity_followups") -> int:
    patch = {
        "auto_closed_follow_up": True,
        "auto_closed_reason": "opportunity_already_terminal",
        "auto_closed_by": actor,
    }
    with engine.begin() as conn:
        result = conn.execute(text("""
            UPDATE tasks
            SET status = 'skipped',
                done_at = COALESCE(done_at, now()),
                done_by = CAST(:actor AS TEXT),
                updated_at = now(),
                metadata = COALESCE(metadata, '{}'::jsonb) || CAST(:metadata_patch AS JSONB)
            WHERE id = CAST(:task_id AS UUID)
              AND status = 'pending'
              AND action_code IN (
                'FOLLOW_UP_QUOTE',
                'FOLLOW_UP_PROFORMA',
                'FOLLOW_UP_PAYMENT',
                'FOLLOW_UP_CUSTOMER_REVIEW',
                'FOLLOW_UP_GENERIC'
              )
        """), {"task_id": task_id, "actor": actor, "metadata_patch": _json(patch)})
    return int(getattr(result, "rowcount", 0) or 0)


def main() -> int:
    parser = argparse.ArgumentParser()
    parser.add_argument("--apply", action="store_true")
    parser.add_argument("--limit", type=int, default=100)
    args = parser.parse_args()

    rows = find_candidates(args.limit)
    print(f"Follow-ups pendentes em oportunidades terminais: {len(rows)}")
    updated = 0
    for row in rows:
        print("-", row["task_id"], row["action_code"], row["opportunity_stage"], row["opportunity_status"], row["opportunity_title"])
        if args.apply:
            updated += apply_candidate(row["task_id"])
    if args.apply:
        print(f"updated: {updated}")
    else:
        print("dry-run: usa --apply para fechar estes follow-ups.")
    return 1 if rows and not args.apply else 0


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