#!/usr/bin/env python3
import json
import os
import sys
import urllib.request
import urllib.error
from pathlib import Path
from typing import Any, Dict, List

try:
    import psycopg
except Exception:
    psycopg = None


def env(name: str, default: str = "") -> str:
    return os.getenv(name, default).strip()


API_URL = env("EVAL_API_URL", "http://127.0.0.1:8020/analyze")
DATASET = Path(env("EVAL_DATASET", "data/action_eval_cases.jsonl"))
PSQL_DATABASE_URL = env("PSQL_DATABASE_URL")
EVAL_CLEANUP = env("EVAL_CLEANUP", "true").lower() == "true"
EVAL_LIMIT = int(env("EVAL_LIMIT", "0") or "0")
EVAL_LLM_ONLY = env("EVAL_LLM_ONLY", "false").lower() == "true"


def load_cases() -> List[Dict[str, Any]]:
    cases = []
    with DATASET.open("r", encoding="utf-8") as f:
        for line in f:
            line = line.strip()
            if line:
                cases.append(json.loads(line))

    if EVAL_LIMIT > 0:
        return cases[:EVAL_LIMIT]

    return cases


def post_json(url: str, payload: Dict[str, Any]) -> Dict[str, Any]:
    raw = json.dumps(payload, ensure_ascii=False).encode("utf-8")

    req = urllib.request.Request(
        url,
        data=raw,
        headers={"Content-Type": "application/json"},
        method="POST",
    )

    try:
        with urllib.request.urlopen(req, timeout=90) as resp:
            body = resp.read().decode("utf-8", errors="replace")
            return {
                "ok": 200 <= resp.status < 300,
                "status": resp.status,
                "json": json.loads(body) if body else {},
                "raw": body,
            }
    except urllib.error.HTTPError as e:
        body = e.read().decode("utf-8", errors="replace")
        return {
            "ok": False,
            "status": e.code,
            "json": None,
            "raw": body,
        }


def cleanup_eval_data() -> None:
    if not EVAL_CLEANUP or not PSQL_DATABASE_URL or psycopg is None:
        return

    # Limpeza defensiva: algumas tabelas não têm source_system/source_event_id.
    # Por isso verificamos as colunas reais antes de construir o DELETE.
    tables = [
        "integration_outbox",
        "tasks",
        "action_runs",
        "messages",
        "raw_events",
    ]

    def table_columns(cur, table: str) -> set[str]:
        cur.execute(
            """
            select column_name
            from information_schema.columns
            where table_name = %s
            """,
            (table,),
        )
        return {row[0] for row in cur.fetchall()}

    with psycopg.connect(PSQL_DATABASE_URL) as conn:
        with conn.cursor() as cur:
            for table in tables:
                cols = table_columns(cur, table)
                conditions = []

                if "source_system" in cols:
                    conditions.append("source_system like 'action_eval%'")

                if "conversation_id" in cols:
                    conditions.append("conversation_id like 'eval-%'")

                if "contact_id" in cols:
                    conditions.append("contact_id like 'eval-contact-%'")

                if "source_event_id" in cols:
                    conditions.append("source_event_id like 'eval-%'")

                if "idempotency_key" in cols:
                    conditions.append("idempotency_key like '%eval-%'")

                if "metadata" in cols:
                    conditions.append("metadata::text like '%action_eval%'")

                if not conditions:
                    continue

                sql = f"delete from {table} where " + " or ".join(conditions)
                cur.execute(sql)

        conn.commit()


def main() -> int:
    cases = load_cases()

    cleanup_eval_data()

    results = []
    ok_count = 0

    print(f"Dataset: {DATASET}")
    print(f"Cases: {len(cases)}")
    print(f"API: {API_URL}")
    print(f"EVAL_LLM_ONLY={EVAL_LLM_ONLY}")
    print("---")

    for i, case in enumerate(cases, start=1):
        conv_id = f"eval-{case['id']}"
        payload = {
            "last_customer_message": case["message"],
            "previous_context": case.get("context", ""),
            "source": "action_eval_llm_only" if EVAL_LLM_ONLY else "action_eval",
            "conversation_id": conv_id,
            "contact_id": f"eval-contact-{i}",
        }

        result = post_json(API_URL, payload)

        if not result["ok"]:
            got = "HTTP_ERROR"
            confidence = None
            provider = None
            note = result["raw"][:300]
        else:
            data = result["json"] or {}
            decision = data.get("action_decision") or {}
            usage = data.get("usage") or {}
            got = decision.get("action_code") or data.get("action_result", {}).get("action_code") or "UNKNOWN"
            confidence = decision.get("confidence")
            provider = usage.get("provider")
            note = decision.get("note")

        expected = case["expected"]
        passed = got == expected
        ok_count += 1 if passed else 0

        results.append({
            "id": case["id"],
            "expected": expected,
            "got": got,
            "ok": passed,
            "confidence": confidence,
            "provider": provider,
            "note": note,
        })

        status = "OK" if passed else "FAIL"
        print(f"{status:4} {case['id']}")
        print(f"     expected={expected}")
        print(f"     got     ={got}")
        print(f"     conf    ={confidence} provider={provider}")
        if not passed:
            print(f"     note    ={note}")
        print("---")

    accuracy = ok_count / len(cases) if cases else 0

    print(f"accuracy={accuracy:.1%} ({ok_count}/{len(cases)})")

    by_expected = {}
    for r in results:
        item = by_expected.setdefault(r["expected"], {"ok": 0, "total": 0})
        item["total"] += 1
        item["ok"] += 1 if r["ok"] else 0

    print("--- by expected")
    for code, stats in sorted(by_expected.items()):
        print(f"{code}: {stats['ok']}/{stats['total']}")

    Path("data/action_eval_last_results.json").write_text(
        json.dumps(results, ensure_ascii=False, indent=2),
        encoding="utf-8",
    )

    cleanup_eval_data()

    return 0 if accuracy >= 0.90 else 1


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