#!/usr/bin/env python3
"""Cloud-style zero-configuration smoke test.

Simulates the cloud agent invoking the 4 insurance skills with **only**
the wrapper script paths documented in each ``SKILL.md`` — no PYTHONPATH,
no skill-specific env vars, an unrelated CWD, and a clean ``os.environ``
that explicitly drops ``PYTHONPATH`` if present.

Each skill is exercised with one representative read + one write to
prove the SQLite path defaults work without any setup, then
``dashboard_all.py`` is run to verify cross-skill aggregation works
through the wrappers.

Usage::

    python3 skills/insurance-customer-policy/src/scripts/test_cloud_zero_config.py
"""
from __future__ import annotations

import json
import os
import subprocess
import sys
import tempfile
import time
from pathlib import Path

HERE = Path(__file__).resolve().parent  # .../insurance-customer-policy/src/scripts
SKILLS_ROOT = HERE.parent.parent.parent  # .../skills

CUSTOMER_CLI = SKILLS_ROOT / "insurance-customer-policy" / "src" / "scripts" / "cli.py"
PRODUCT_CLI = SKILLS_ROOT / "insurance-product-analysis" / "src" / "scripts" / "cli.py"
SCHEDULE_CLI = SKILLS_ROOT / "insurance-schedule-renewal" / "src" / "scripts" / "cli.py"
PIPELINE_CLI = SKILLS_ROOT / "insurance-sales-pipeline" / "src" / "scripts" / "cli.py"
DASHBOARD_ALL = HERE / "dashboard_all.py"


def _clean_env(extra: dict[str, str] | None = None) -> dict[str, str]:
    """Build a minimal env that mimics cloud: no PYTHONPATH, only required vars."""
    env: dict[str, str] = {}
    for var in ("PATH", "SystemRoot", "TEMP", "TMP", "HOME", "USERPROFILE", "windir", "ComSpec"):
        if var in os.environ:
            env[var] = os.environ[var]
    env["PYTHONIOENCODING"] = "utf-8"
    if extra:
        env.update(extra)
    return env


def run(cli: Path, args: list[str], extra_env: dict[str, str] | None = None,
        cwd: Path | None = None) -> dict:
    """Run wrapper CLI with a clean env and arbitrary CWD; return parsed JSON."""
    env = _clean_env(extra_env)
    r = subprocess.run(
        [sys.executable, str(cli), *args],
        cwd=str(cwd) if cwd else None,
        env=env,
        capture_output=True,
        text=True,
        encoding="utf-8",
        timeout=60,
    )
    if r.returncode not in (0, 1):
        raise AssertionError(
            f"\n{cli.name} {' '.join(args)} crashed (rc={r.returncode})\n"
            f"STDOUT:\n{r.stdout}\nSTDERR:\n{r.stderr}"
        )
    out = (r.stdout or "").strip()
    assert out, f"{cli.name} {' '.join(args)} produced no stdout. STDERR:\n{r.stderr}"
    try:
        return json.loads(out)
    except json.JSONDecodeError as e:
        raise AssertionError(f"{cli.name} {' '.join(args)} bad JSON: {e}\nSTDOUT:\n{out}")


def main() -> int:
    print(f"[cloud-smoke] starting; CWD will be a tempdir; env will drop PYTHONPATH")
    print(f"[cloud-smoke] CUSTOMER_CLI = {CUSTOMER_CLI}")

    assert CUSTOMER_CLI.is_file(), f"missing wrapper: {CUSTOMER_CLI}"
    assert PRODUCT_CLI.is_file(), f"missing wrapper: {PRODUCT_CLI}"
    assert SCHEDULE_CLI.is_file(), f"missing wrapper: {SCHEDULE_CLI}"
    assert PIPELINE_CLI.is_file(), f"missing wrapper: {PIPELINE_CLI}"
    assert DASHBOARD_ALL.is_file(), f"missing aggregator: {DASHBOARD_ALL}"

    with tempfile.TemporaryDirectory() as raw:
        tmp = Path(raw)
        (tmp / "data").mkdir()

        # Use isolated shared DB under the tempdir so the test never touches
        # the user's real ~/.config dirs. 四个 skill 共用一库。
        ts = int(time.time() * 1000)
        shared_db = tmp / "data" / f"insurance-{ts}.sqlite3"

        env_dbs = {
            "INSURANCE_DB_PATH": str(shared_db),
            "INSURANCE_CUSTOMER_DB_PATH": str(shared_db),
            "INSURANCE_PRODUCT_DB_PATH": str(shared_db),
            "INSURANCE_SCHEDULE_DB_PATH": str(shared_db),
            "INSURANCE_PIPELINE_DB_PATH": str(shared_db),
        }

        # --- 1. customer skill: zero-config write + read ---
        print("\n[1/6] customer skill via wrapper (cwd=tempdir, no PYTHONPATH)...")
        d = run(CUSTOMER_CLI,
                ["customer-add", "--name", "云客户", "--phone", "13900000001",
                 "--age", "35", "--income", "300000"],
                extra_env=env_dbs, cwd=tmp)
        assert d.get("ok"), f"customer-add failed: {d}"
        cust_id = d.get("id") or d.get("customer_id")
        assert cust_id, f"customer-add missing id field: {d}"
        d = run(CUSTOMER_CLI, ["customer-query", "--customer-id", cust_id],
                extra_env=env_dbs, cwd=tmp)
        assert d.get("ok") and d.get("count", 0) >= 1, d
        assert d["customers"][0]["name"] == "云客户", d
        print(f"   ok customer={cust_id}")

        # --- 2. customer dashboard ---
        d = run(CUSTOMER_CLI, ["dashboard", "--json"], extra_env=env_dbs, cwd=tmp)
        assert d.get("ok") and d.get("customers_total", 0) >= 1, d
        print(f"   ok dashboard customers_total={d['customers_total']}")

        # --- 3. product skill: zero-config write + read ---
        print("\n[2/6] product skill via wrapper...")
        d = run(PRODUCT_CLI,
                ["product-add", "--name", "云重疾", "--company", "云保险",
                 "--type", "重疾险", "--coverage-range", "10万-100万",
                 "--premium-range", "3000-15000", "--age-range", "0-55岁"],
                extra_env=env_dbs, cwd=tmp)
        assert d.get("ok"), f"product-add failed: {d}"
        # 共享库模式下，gap-analysis 直接查共享库中的客户/保单数据。
        d = run(PRODUCT_CLI,
                ["gap-analysis", "--customer-id", cust_id],
                extra_env=env_dbs, cwd=tmp)
        assert d.get("ok"), f"product gap-analysis failed: {d}"
        print(f"   ok product-add + gap-analysis")

        # --- 4. schedule skill ---
        print("\n[3/6] schedule skill via wrapper...")
        d = run(SCHEDULE_CLI, ["renewal-query", "--days", "30"],
                extra_env=env_dbs, cwd=tmp)
        assert d.get("ok"), f"schedule renewal-query failed: {d}"
        d = run(SCHEDULE_CLI, ["dashboard", "--json"],
                extra_env=env_dbs, cwd=tmp)
        assert d.get("ok"), f"schedule dashboard failed: {d}"
        print(f"   ok schedule renewal-query + dashboard")

        # --- 5. pipeline skill ---
        print("\n[4/6] pipeline skill via wrapper...")
        d = run(PIPELINE_CLI,
                ["deal-add", "--customer-id", cust_id, "--product", "云重疾",
                 "--product-type", "重疾险", "--stage", "在谈",
                 "--estimated-premium", "8000"],
                extra_env=env_dbs, cwd=tmp)
        assert d.get("ok"), f"deal-add failed: {d}"
        d = run(PIPELINE_CLI, ["dashboard", "--json"], extra_env=env_dbs, cwd=tmp)
        assert d.get("ok"), f"pipeline dashboard failed: {d}"
        print(f"   ok pipeline deal-add + dashboard")

        # --- 6. cross-skill aggregation via dashboard_all.py ---
        print("\n[5/6] dashboard_all aggregator via direct path...")
        env = _clean_env(env_dbs)
        r = subprocess.run(
            [sys.executable, str(DASHBOARD_ALL)],
            cwd=str(tmp),
            env=env,
            capture_output=True,
            text=True,
            encoding="utf-8",
            timeout=60,
        )
        assert r.returncode == 0, f"dashboard_all rc={r.returncode}\nSTDERR:\n{r.stderr}"
        agg = json.loads(r.stdout)
        assert agg.get("ok"), f"dashboard_all not ok: {agg}"
        assert agg["overview"]["customers_total"] >= 1, agg["overview"]
        assert agg["overview"]["products_total"] >= 1, agg["overview"]
        # 兼容旧版/新版聚合结构：
        # - 旧版 skills: {skill_name: {ok: true, ...}}
        # - 新版 skills: {"source": "shared-db"}
        if isinstance(agg.get("skills"), dict):
            for skill_name, skill_res in agg["skills"].items():
                if isinstance(skill_res, dict):
                    assert skill_res.get("ok"), f"{skill_name} sub-result not ok: {skill_res}"
        print(f"   ok aggregator: {agg['overview']}")

        # --- 7. verify default DB path (no env) creates dir under ~/.config ---
        print("\n[6/6] default DB path probe (no env vars)...")
        # Use a customized HOME so we don't pollute the real one.
        fake_home = tmp / "fake_home"
        fake_home.mkdir()
        env_no_db = _clean_env()
        if sys.platform == "win32":
            env_no_db["USERPROFILE"] = str(fake_home)
        env_no_db["HOME"] = str(fake_home)
        r = subprocess.run(
            [sys.executable, str(CUSTOMER_CLI), "customer-query"],
            cwd=str(tmp),
            env=env_no_db,
            capture_output=True,
            text=True,
            encoding="utf-8",
            timeout=30,
        )
        assert r.returncode == 0, f"default DB customer-query failed:\n{r.stderr}"
        out = json.loads(r.stdout)
        assert out.get("ok"), out
        # Default path must have been created under fake_home/.config
        expected_dir = fake_home / ".config" / "insurance"
        assert expected_dir.is_dir(), f"default DB dir not created: {expected_dir}"
        print(f"   ok default DB dir auto-created at {expected_dir}")

    print("\nAll cloud-style zero-configuration smoke tests passed.")
    return 0


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