#!/usr/bin/env python3
"""Repair safe opportunity-detail inconsistencies.

Fixes the class of issues seen after Chatwoot recovery:
- payment is confirmed but old pending payment/follow-up tasks still drive the UI;
- fiscal data can be safely completed from the linked Jasmin document.

By default this is a dry-run. Use --apply to write changes.
"""
from __future__ import annotations

import argparse
from typing import Any

from sqlalchemy import text

from app.db import engine
from app.task_service import mark_obsolete_payment_followup_tasks
from app.jasmin_fiscal_sync_service import apply_jasmin_fiscal_sync, get_jasmin_fiscal_sync_preview


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


def _scalar(sql: str, params: dict[str, Any]) -> Any:
    with engine.begin() as conn:
        return conn.execute(text(sql), params).scalar()


def _select_opportunities(args: argparse.Namespace) -> list[dict[str, Any]]:
    if args.opportunity_id:
        return _rows("""
            SELECT id::text, title, conversation_id
            FROM opportunities
            WHERE id = CAST(:id AS UUID)
        """, {"id": args.opportunity_id})
    if args.conversation_id:
        return _rows("""
            SELECT id::text, title, conversation_id
            FROM opportunities
            WHERE conversation_id = :conversation_id
              AND COALESCE(status, 'open') <> 'closed'
            ORDER BY updated_at DESC
        """, {"conversation_id": str(args.conversation_id)})
    if args.document_number:
        return _rows("""
            SELECT o.id::text, o.title, o.conversation_id
            FROM opportunities o
            JOIN commercial_documents d ON d.opportunity_id = o.id
            WHERE d.document_number = :document_number
              AND COALESCE(o.status, 'open') <> 'closed'
            GROUP BY o.id, o.title, o.conversation_id
            ORDER BY MAX(o.updated_at) DESC
        """, {"document_number": str(args.document_number)})
    if args.all_open:
        return _rows("""
            SELECT DISTINCT o.id::text, o.title, o.conversation_id
            FROM opportunities o
            LEFT JOIN operation_links p
              ON p.opportunity_id = o.id
             AND p.system = 'clientflow'
             AND p.external_type = 'payment'
             AND p.status = 'confirmed'
            LEFT JOIN tasks t
              ON t.opportunity_id = o.id
             AND t.status = 'pending'
             AND upper(COALESCE(t.action_code,'')) IN ('CONFIRM_PAYMENT','FOLLOW_UP_PAYMENT','FOLLOW_UP_PROFORMA','FOLLOW_UP_QUOTE')
            LEFT JOIN commercial_documents d
              ON d.opportunity_id = o.id
             AND d.system = 'jasmin'
             AND COALESCE(d.is_active, TRUE) = TRUE
            LEFT JOIN customers c ON c.id = o.local_customer_id
            WHERE COALESCE(o.status, 'open') <> 'closed'
              AND (
                ((p.id IS NOT NULL OR o.stage = 'PAYMENT_CONFIRMED') AND t.id IS NOT NULL)
                OR (
                  d.id IS NOT NULL AND (
                    o.local_customer_id IS NULL
                    OR COALESCE(c.email,'') = ''
                    OR COALESCE(c.street_name,'') = ''
                    OR COALESCE(c.postal_zone,'') = ''
                    OR COALESCE(c.city_name,'') = ''
                  )
                )
              )
            ORDER BY o.title NULLS LAST, o.id
            LIMIT :limit
        """, {"limit": int(args.limit or 200)})
    raise SystemExit("Use --opportunity-id, --conversation-id, --document-number or --all-open")


def _payment_confirmed(opportunity_id: str) -> bool:
    return bool(_scalar("""
        SELECT 1
        FROM opportunities o
        LEFT JOIN operation_links p
          ON p.opportunity_id = o.id
         AND p.system = 'clientflow'
         AND p.external_type = 'payment'
         AND p.status = 'confirmed'
        WHERE o.id = CAST(:id AS UUID)
          AND (p.id IS NOT NULL OR o.stage = 'PAYMENT_CONFIRMED')
        LIMIT 1
    """, {"id": opportunity_id}))


def _obsolete_pending_count(opportunity_id: str) -> int:
    return int(_scalar("""
        SELECT COUNT(*)
        FROM tasks
        WHERE opportunity_id = CAST(:id AS UUID)
          AND status = 'pending'
          AND upper(COALESCE(action_code,'')) IN ('CONFIRM_PAYMENT','FOLLOW_UP_PAYMENT','FOLLOW_UP_PROFORMA','FOLLOW_UP_QUOTE')
    """, {"id": opportunity_id}) or 0)


def main() -> int:
    parser = argparse.ArgumentParser(description="Repair safe opportunity consistency issues.")
    parser.add_argument("--opportunity-id")
    parser.add_argument("--conversation-id")
    parser.add_argument("--document-number")
    parser.add_argument("--all-open", action="store_true")
    parser.add_argument("--limit", type=int, default=200)
    parser.add_argument("--apply", action="store_true", help="Write safe repairs. Without this, only reports.")
    parser.add_argument("--complete-fiscal-from-jasmin", action="store_true", help="Also fill empty fiscal fields from linked Jasmin documents when safe.")
    args = parser.parse_args()

    opportunities = _select_opportunities(args)
    print(f"opportunities={len(opportunities)} apply={args.apply} complete_fiscal_from_jasmin={args.complete_fiscal_from_jasmin}")
    total_closed = 0
    total_fiscal = 0

    for opp in opportunities:
        oid = str(opp.get("id"))
        title = opp.get("title") or oid
        payment_ok = _payment_confirmed(oid)
        obsolete_count = _obsolete_pending_count(oid) if payment_ok else 0
        print("\n" + "=" * 80)
        print(f"{title}")
        print(f"id={oid} conversation={opp.get('conversation_id') or '—'}")
        print(f"payment_confirmed={payment_ok} obsolete_payment_tasks={obsolete_count}")

        if args.apply and payment_ok and obsolete_count:
            closed = mark_obsolete_payment_followup_tasks(oid, actor="repair_opportunity_consistency")
            total_closed += closed
            print(f"closed_obsolete_tasks={closed}")

        try:
            preview = get_jasmin_fiscal_sync_preview(oid)
        except Exception as exc:
            print(f"jasmin_fiscal_preview_error={exc}")
            preview = {"available": False}
        if preview.get("available"):
            print(f"jasmin_available=True conflict={bool(preview.get('conflict'))} fillable={preview.get('fillable_fields') or []} missing={preview.get('missing_fields') or []}")
            if args.apply and args.complete_fiscal_from_jasmin and not preview.get("conflict") and (preview.get("fillable_fields") or not preview.get("linked_customer_id")):
                try:
                    result = apply_jasmin_fiscal_sync(oid, actor="repair_opportunity_consistency")
                    total_fiscal += 1
                    print(f"fiscal_sync_applied=True filled={result.get('filled_fields') or []} customer_id={result.get('customer_id')}")
                except Exception as exc:
                    print(f"fiscal_sync_error={exc}")
        else:
            print(f"jasmin_available=False reason={preview.get('reason') or 'unknown'}")

    print("\nSUMMARY")
    print(f"closed_obsolete_tasks={total_closed}")
    print(f"fiscal_sync_applied={total_fiscal}")
    return 0


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