"""
yearly_targets 写权限收紧 — 冒烟测试脚本

幂等，可重复跑。
确保后端已启动: uvicorn app.main:app --reload

12 步覆盖:
1. zhipianren POST → 201
2. zebian POST → 201
3. biandao POST → 403
4. 未登录 POST → 401
5. zhipianren PATCH → 200
6. zebian PATCH → 200
7. biandao PATCH → 403
8. zhipianren DELETE → 200
9. zebian DELETE → 403
10. biandao DELETE → 403
11. biandao GET 列表 → 200
12. zhipianren GET 列表 → 200
"""

import sys
from pathlib import Path

backend_dir = Path(__file__).resolve().parent.parent
sys.path.insert(0, str(backend_dir))

import httpx

BASE = "http://localhost:8000"
HEADERS = {"Content-Type": "application/json"}


def login(username: str, password: str) -> str:
    """登录并返回 session cookie。"""
    r = httpx.post(f"{BASE}/api/auth/login", json={"username": username, "password": password})
    r.raise_for_status()
    return r.cookies.get("milsci_session", "")


def step(name: str, expected_status: int, method: str, url: str, **kwargs):
    """执行一个 HTTP 步骤，比较实际状态码与期望值。"""
    cookies = kwargs.pop("cookies", {})
    if method == "GET":
        r = httpx.get(url, cookies=cookies, timeout=10)
    elif method == "POST":
        r = httpx.post(url, cookies=cookies, headers=HEADERS, timeout=10, **kwargs)
    elif method == "PATCH":
        r = httpx.patch(url, cookies=cookies, headers=HEADERS, timeout=10, **kwargs)
    elif method == "DELETE":
        r = httpx.delete(url, cookies=cookies, timeout=10)
    else:
        raise ValueError(method)

    status_ok = r.status_code == expected_status
    mark = "[OK]" if status_ok else f"[FAIL expected {expected_status} got {r.status_code}]"
    print(f"  {mark} [{method}] {name}")
    if not status_ok:
        print(f"       Response: {r.text[:300]}")
    return r, status_ok


def ensure_user(cookies: dict, username: str, display_name: str, role: str, password: str = "Test1234"):
    """幂等创建测试账号（如果不存在）。"""
    # 先尝试登录（账号可能已存在）
    try:
        r = httpx.get(f"{BASE}/api/users/me", cookies=cookies, timeout=5)
        if r.status_code == 200:
            me = r.json()
            if me.get("username") == username:
                print(f"  [SKIP] user {username} already exists, reusing")
                return
    except Exception:
        pass

    # 尝试创建
    r = httpx.post(
        f"{BASE}/api/users",
        json={"username": username, "display_name": display_name, "password": password, "role": role},
        cookies=cookies,
        headers=HEADERS,
        timeout=10,
    )
    if r.status_code == 201:
        print(f"  [OK] created {username} ({role})")
    elif r.status_code == 409:
        print(f"  [SKIP] {username} already exists")
    else:
        print(f"  [WARN] create {username} returned {r.status_code}: {r.text[:100]}")


def cleanup_test_target(cookies: dict):
    """清理测试用年度目标（2027），幂等。"""
    try:
        r = httpx.get(f"{BASE}/api/yearly_targets", cookies=cookies, timeout=10)
        if r.status_code == 200:
            for t in r.json():
                if t.get("year") == 2027:
                    httpx.delete(f"{BASE}/api/yearly_targets/{t['id']}", cookies=cookies, timeout=10)
                    print(f"  [CLEAN] removed existing 2027 target ID={t['id']}")
    except Exception:
        pass


def main():
    print("=" * 60)
    print("  yearly_targets 写权限收紧 — 冒烟测试")
    print("=" * 60)

    # Step 0: 用 zhipianren 登录，准备测试账号
    print("\n[Step 0] Login as zhipianren and ensure test accounts exist")
    try:
        admin_cookie = login("simonkoson", "liutong65")
    except Exception as e:
        print(f"  [FAIL] cannot login as simonkoson: {e}")
        return

    admin_cookies = {"milsci_session": admin_cookie}
    print("  [OK] logged in as simonkoson (zhipianren)")

    # 幂等创建 zebian / biandao 测试账号
    ensure_user(admin_cookies, "test_zebian_smoke", "TestZebra Smoke", "zebian")
    ensure_user(admin_cookies, "test_biandao_smoke", "TestBiandao Smoke", "biandao")

    # 登录 zebian 和 biandao 获取 cookie
    print("\n[Step 0b] Login as zebian and biandao")
    zebian_cookie = login("test_zebian_smoke", "Test1234")
    zebian_cookies = {"milsci_session": zebian_cookie}
    print("  [OK] logged in as test_zebian_smoke (zebian)")

    biandao_cookie = login("test_biandao_smoke", "Test1234")
    biandao_cookies = {"milsci_session": biandao_cookie}
    print("  [OK] logged in as test_biandao_smoke (biandao)")

    # 清理可能残留的 2027 目标
    cleanup_test_target(admin_cookies)

    results = []

    # =====================================================================
    # Step 1: zhipianren POST → 201
    # =====================================================================
    print("\n[Step 1] POST new yearly target — zhipianren → 201")
    r, ok = step(
        "POST /api/yearly_targets (zhipianren)",
        201,
        "POST",
        f"{BASE}/api/yearly_targets",
        json={"year": 2027, "base_target": 0.66, "stretch_target": 0.91},
        cookies=admin_cookies,
    )
    target_id = r.json().get("id") if ok else None
    results.append(("Step 1: zhipianren POST 201", ok))

    # =====================================================================
    # Step 2: zebian POST → 201
    # =====================================================================
    print("\n[Step 2] POST new yearly target — zebian → 201")
    # 清理 2027（刚被 zhipianren 创建了）
    cleanup_test_target(admin_cookies)
    r, ok = step(
        "POST /api/yearly_targets (zebian)",
        201,
        "POST",
        f"{BASE}/api/yearly_targets",
        json={"year": 2027, "base_target": 0.66, "stretch_target": 0.91},
        cookies=zebian_cookies,
    )
    target_id2 = r.json().get("id") if ok else None
    results.append(("Step 2: zebian POST 201", ok))

    # =====================================================================
    # Step 3: biandao POST → 403
    # =====================================================================
    print("\n[Step 3] POST new yearly target — biandao → 403")
    cleanup_test_target(admin_cookies)
    r, ok = step(
        "POST /api/yearly_targets (biandao)",
        403,
        "POST",
        f"{BASE}/api/yearly_targets",
        json={"year": 2027, "base_target": 0.66, "stretch_target": 0.91},
        cookies=biandao_cookies,
    )
    results.append(("Step 3: biandao POST 403", ok))

    # =====================================================================
    # Step 4: 未登录 POST → 401
    # =====================================================================
    print("\n[Step 4] POST new yearly target — not logged in → 401")
    r, ok = step(
        "POST /api/yearly_targets (no auth)",
        401,
        "POST",
        f"{BASE}/api/yearly_targets",
        json={"year": 2027, "base_target": 0.66, "stretch_target": 0.91},
        cookies={},
    )
    results.append(("Step 4: no auth POST 401", ok))

    # =====================================================================
    # Step 5: zhipianren PATCH → 200
    # =====================================================================
    print("\n[Step 5] PATCH yearly target — zhipianren → 200")
    # 先用 admin 建一个可 PATCH 的目标
    cleanup_test_target(admin_cookies)
    r = httpx.post(
        f"{BASE}/api/yearly_targets",
        json={"year": 2027, "base_target": 0.66, "stretch_target": 0.91},
        cookies=admin_cookies,
        headers=HEADERS,
        timeout=10,
    )
    patch_id = r.json().get("id") if r.status_code == 201 else None

    if patch_id:
        r, ok = step(
            "PATCH /api/yearly_targets/{id} (zhipianren)",
            200,
            "PATCH",
            f"{BASE}/api/yearly_targets/{patch_id}",
            json={"base_target": 0.67},
            cookies=admin_cookies,
        )
    else:
        ok = False
        print("  [FAIL] could not create target for PATCH test")
    results.append(("Step 5: zhipianren PATCH 200", ok))

    # =====================================================================
    # Step 6: zebian PATCH → 200
    # =====================================================================
    print("\n[Step 6] PATCH yearly target — zebian → 200")
    cleanup_test_target(admin_cookies)
    r = httpx.post(
        f"{BASE}/api/yearly_targets",
        json={"year": 2027, "base_target": 0.66, "stretch_target": 0.91},
        cookies=admin_cookies,
        headers=HEADERS,
        timeout=10,
    )
    patch_id2 = r.json().get("id") if r.status_code == 201 else None

    if patch_id2:
        r, ok = step(
            "PATCH /api/yearly_targets/{id} (zebian)",
            200,
            "PATCH",
            f"{BASE}/api/yearly_targets/{patch_id2}",
            json={"stretch_target": 0.92},
            cookies=zebian_cookies,
        )
    else:
        ok = False
        print("  [FAIL] could not create target for PATCH test")
    results.append(("Step 6: zebian PATCH 200", ok))

    # =====================================================================
    # Step 7: biandao PATCH → 403
    # =====================================================================
    print("\n[Step 7] PATCH yearly target — biandao → 403")
    cleanup_test_target(admin_cookies)
    r = httpx.post(
        f"{BASE}/api/yearly_targets",
        json={"year": 2027, "base_target": 0.66, "stretch_target": 0.91},
        cookies=admin_cookies,
        headers=HEADERS,
        timeout=10,
    )
    patch_id3 = r.json().get("id") if r.status_code == 201 else None

    if patch_id3:
        r, ok = step(
            "PATCH /api/yearly_targets/{id} (biandao)",
            403,
            "PATCH",
            f"{BASE}/api/yearly_targets/{patch_id3}",
            json={"base_target": 0.68},
            cookies=biandao_cookies,
        )
    else:
        ok = False
        print("  [FAIL] could not create target for PATCH test")
    results.append(("Step 7: biandao PATCH 403", ok))

    # =====================================================================
    # Step 8: zhipianren DELETE → 200
    # =====================================================================
    print("\n[Step 8] DELETE yearly target — zhipianren → 200")
    cleanup_test_target(admin_cookies)
    r = httpx.post(
        f"{BASE}/api/yearly_targets",
        json={"year": 2027, "base_target": 0.66, "stretch_target": 0.91},
        cookies=admin_cookies,
        headers=HEADERS,
        timeout=10,
    )
    del_id = r.json().get("id") if r.status_code == 201 else None

    if del_id:
        r, ok = step(
            "DELETE /api/yearly_targets/{id} (zhipianren)",
            204,
            "DELETE",
            f"{BASE}/api/yearly_targets/{del_id}",
            cookies=admin_cookies,
        )
    else:
        ok = False
        print("  [FAIL] could not create target for DELETE test")
    results.append(("Step 8: zhipianren DELETE 204", ok))

    # =====================================================================
    # Step 9: zebian DELETE → 403
    # =====================================================================
    print("\n[Step 9] DELETE yearly target — zebian → 403")
    cleanup_test_target(admin_cookies)
    r = httpx.post(
        f"{BASE}/api/yearly_targets",
        json={"year": 2027, "base_target": 0.66, "stretch_target": 0.91},
        cookies=admin_cookies,
        headers=HEADERS,
        timeout=10,
    )
    del_id2 = r.json().get("id") if r.status_code == 201 else None

    if del_id2:
        r, ok = step(
            "DELETE /api/yearly_targets/{id} (zebian)",
            403,
            "DELETE",
            f"{BASE}/api/yearly_targets/{del_id2}",
            cookies=zebian_cookies,
        )
    else:
        ok = False
        print("  [FAIL] could not create target for DELETE test")
    results.append(("Step 9: zebian DELETE 403", ok))

    # =====================================================================
    # Step 10: biandao DELETE → 403
    # =====================================================================
    print("\n[Step 10] DELETE yearly target — biandao → 403")
    cleanup_test_target(admin_cookies)
    r = httpx.post(
        f"{BASE}/api/yearly_targets",
        json={"year": 2027, "base_target": 0.66, "stretch_target": 0.91},
        cookies=admin_cookies,
        headers=HEADERS,
        timeout=10,
    )
    del_id3 = r.json().get("id") if r.status_code == 201 else None

    if del_id3:
        r, ok = step(
            "DELETE /api/yearly_targets/{id} (biandao)",
            403,
            "DELETE",
            f"{BASE}/api/yearly_targets/{del_id3}",
            cookies=biandao_cookies,
        )
    else:
        ok = False
        print("  [FAIL] could not create target for DELETE test")
    results.append(("Step 10: biandao DELETE 403", ok))

    # =====================================================================
    # Step 11: biandao GET 列表 → 200
    # =====================================================================
    print("\n[Step 11] GET yearly targets list — biandao → 200")
    r, ok = step(
        "GET /api/yearly_targets (biandao)",
        200,
        "GET",
        f"{BASE}/api/yearly_targets",
        cookies=biandao_cookies,
    )
    results.append(("Step 11: biandao GET 200", ok))

    # =====================================================================
    # Step 12: zhipianren GET 列表 → 200
    # =====================================================================
    print("\n[Step 12] GET yearly targets list — zhipianren → 200")
    r, ok = step(
        "GET /api/yearly_targets (zhipianren)",
        200,
        "GET",
        f"{BASE}/api/yearly_targets",
        cookies=admin_cookies,
    )
    results.append(("Step 12: zhipianren GET 200", ok))

    # =====================================================================
    # Summary
    # =====================================================================
    print("\n" + "=" * 60)
    print("  结果汇总")
    print("=" * 60)
    passed = sum(1 for _, ok in results if ok)
    total = len(results)
    for name, ok in results:
        mark = "[OK]" if ok else "[FAIL]"
        print(f"  {mark} {name}")

    print(f"\n  PASS: {passed}/{total}")
    if passed == total:
        print("  ALL PASS")
    else:
        print("  SOME FAILURES")

    print("\n测试账号已保留(供后续手动清理):")
    print("  - test_zebian_smoke (zebian)")
    print("  - test_biandao_smoke (biandao)")

    print("\n" + "=" * 60)


if __name__ == "__main__":
    main()