"""
Task 2 批量导入冒烟脚本 — httpx（幂等版，可重复跑）
确保后端已启动: py -3.12 -c "import sys; sys.path.insert(0, r'E:\tps-dashboard\backend'); import uvicorn; uvicorn.run('app.main:app', host='127.0.0.1', port=8000, reload=False)"

Phase 2 Task 2 实测清单覆盖:
  Step 1: GET  /api/imports/template       → 模板下载
  Step 2: POST /api/imports/episodes       → 合法行单行导入
  Step 3: POST /api/imports/episodes       → 部分失败行（成功+失败混合格式）
  Step 4: GET  /api/imports/errors/{id}    → 失败行 Excel 下载
  Step 5: POST /api/imports/episodes       → 2024年份缺失拦截 → 400
  Step 6: POST /api/imports/episodes       → 重复期次拦截 → 409
  Step 7: POST /api/imports/episodes       → 未知编导软落地 → 200 + editor_id=null
"""

import io
import httpx
import random
from openpyxl import Workbook

BASE = "http://localhost:8000"


def login(username: str = "simonkoson", password: str = "liutong65") -> str:
    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):
    cookies = kwargs.pop("cookies", {})
    if method == "GET":
        r = httpx.get(url, cookies=cookies, timeout=30.0, **kwargs)
    elif method == "POST":
        r = httpx.post(url, cookies=cookies, timeout=30.0, **kwargs)
    elif method == "DELETE":
        r = httpx.delete(url, cookies=cookies, timeout=30.0, **kwargs)
    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 make_excel(rows: list[list]) -> bytes:
    """构建简单 xlsx，rows[0] 为表头，后续为数据。"""
    wb = Workbook()
    ws = wb.active
    for row in rows:
        ws.append(row)
    output = io.BytesIO()
    wb.save(output)
    output.seek(0)
    return output.read()


def cleanup_test_episodes(cookies: dict):
    """清理测试用 episode_number（幂等清理）。"""
    r = httpx.get(f"{BASE}/api/episodes?limit=100", cookies=cookies)
    if r.status_code == 200:
        for ep in r.json():
            num = ep.get("episode_number", 0)
            # 清理本次测试可能用到的期号段
            if num in (99, 100, 101, 102, 200, 301):
                httpx.delete(f"{BASE}/api/episodes/{ep['id']}", cookies=cookies)


def main():
    print("=== Login ===")
    session_cookie = login()
    if not session_cookie:
        print("WARNING: session cookie empty")
        session_cookie = ""
    cookies = {"milsci_session": session_cookie}

    # 先清理旧测试数据（幂等）
    print("\n=== Cleanup old test episodes ===")
    cleanup_test_episodes(cookies)

    suffix = random.randint(1000, 9999)

    # ---- Step 1: 模板下载 ----
    print("\n=== Step 1: Template Download ===")
    r, ok = step(
        "GET /api/imports/template?type=episodes",
        200,
        "GET",
        f"{BASE}/api/imports/template?type=episodes",
        cookies=cookies,
    )
    content_type = r.headers.get("content-type", "")
    is_binary = "application/vnd.openxmlformats" in content_type
    print(f"  {'[OK]' if is_binary else '[FAIL]'} Content-Type: {content_type}")
    print(f"  [INFO] Template downloaded ({len(r.content)} bytes)")

    # ---- Step 2: 合法单行导入 ----
    print("\n=== Step 2: Normal import (1 valid row) ===")
    excel_data = make_excel([
        ["episode_number", "program_name", "air_date", "editor_name",
         "audience_share", "audience_rating", "is_rerun", "notes"],
        [99, "《军事科技》实测专用", "2026-06-01", "张颖",
         0.75, 3.8, "否", "Plan smoke test"],
    ])
    r, ok = step(
        "POST /api/imports/episodes (1 valid row) → 200",
        200,
        "POST",
        f"{BASE}/api/imports/episodes",
        files={"file": ("test_valid.xlsx", excel_data, "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet")},
        cookies=cookies,
    )
    if ok:
        result = r.json()
        print(f"  [INFO] total={result['total_rows']} success={result['success_count']} failed={result['failed_count']}")
        ok = (result["success_count"] == 1 and result["failed_count"] == 0)
        print(f"  {'[OK]' if ok else '[FAIL]'} Row count correct")

    # ---- Step 3: 部分失败行回显 ----
    print("\n=== Step 3: Partial failure (3 rows: 1 ok + 2 bad) ===")
    excel_data = make_excel([
        ["episode_number", "program_name", "air_date", "editor_name",
         "audience_share", "audience_rating", "is_rerun", "notes"],
        [100, "《军事科技》成功行", "2026-07-01", "张颖", 0.71, 3.5, "否", ""],
        [101, "无效日期格式行", "不是日期", "张颖", 0.72, 3.6, "否", ""],
        [102, "非整数期号行", "invalid-ep-num", "张颖", 0.73, 3.7, "否", ""],
    ])
    r, ok = step(
        "POST /api/imports/episodes (1ok+2bad) → 200 with errors",
        200,
        "POST",
        f"{BASE}/api/imports/episodes",
        files={"file": ("test_mixed.xlsx", excel_data, "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet")},
        cookies=cookies,
    )
    batch_id = None
    if ok:
        result = r.json()
        print(f"  [INFO] total={result['total_rows']} success={result['success_count']} failed={result['failed_count']}")
        ok_count = (result["success_count"] == 1 and result["failed_count"] == 2)
        print(f"  {'[OK]' if ok_count else '[FAIL]'} Failure count correct")
        # 检查 row_number 是否对齐 Excel 行号（第2行=Excel第2行，第3行=Excel第3行，第4行=Excel第4行）
        if result["errors"]:
            row_nums = sorted([e["row_number"] for e in result["errors"]])
            print(f"  [INFO] error row_numbers: {row_nums}")
            ok_rows = (row_nums == [3, 4])
            print(f"  {'[OK]' if ok_rows else '[FAIL]'} row_number对齐Excel自然行号(表头第1行,数据从第2行起算)")
        batch_id = result.get("batch_id")
        if batch_id:
            print(f"  [INFO] batch_id={batch_id}")

    # ---- Step 4: 失败行下载 ----
    print("\n=== Step 4: Error Excel Download ===")
    if batch_id:
        r, ok = step(
            f"GET /api/imports/errors/{batch_id} → 200 binary",
            200,
            "GET",
            f"{BASE}/api/imports/errors/{batch_id}",
            cookies=cookies,
        )
        ct = r.headers.get("content-type", "")
        is_xlsx = "application/vnd.openxmlformats" in ct
        print(f"  {'[OK]' if is_xlsx else '[FAIL]'} Content-Type: {ct}")
        print(f"  [INFO] Error Excel downloaded ({len(r.content)} bytes)")
    else:
        print("  [SKIP] No batch_id from step 3")

    # ---- Step 5: 年份目标缺失拦截 ----
    print("\n=== Step 5: Missing yearly target → 400 ===")
    excel_data = make_excel([
        ["episode_number", "program_name", "air_date", "editor_name",
         "audience_share", "audience_rating", "is_rerun", "notes"],
        [200, "《军事科技》跨年测试", "2024-01-15", "张颖", 0.68, 3.2, "否", ""],
    ])
    r, ok = step(
        "POST with 2024 air_date (no 2024 target) → 400",
        400,
        "POST",
        f"{BASE}/api/imports/episodes",
        files={"file": ("test_2024.xlsx", excel_data, "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet")},
        cookies=cookies,
    )
    if ok:
        detail = r.json().get("detail", "")
        has_year_target = "年度目标" in detail and "2024" in detail
        print(f"  {'[OK]' if has_year_target else '[FAIL]'} detail包含'年度目标'+'2024': {detail[:100]}")

    # ---- Step 6: 重复期次拦截 ----
    print("\n=== Step 6: Duplicate episode_number ===")
    excel_data = make_excel([
        ["episode_number", "program_name", "air_date", "editor_name",
         "audience_share", "audience_rating", "is_rerun", "notes"],
        [99, "《军事科技》重复期次", "2026-06-15", "张颖", 0.76, 3.9, "否", ""],
    ])
    r, ok = step(
        "POST episode_number=99 (duplicate in same year) → 409 or row-level rejection",
        200,
        "POST",
        f"{BASE}/api/imports/episodes",
        files={"file": ("test_duplicate.xlsx", excel_data, "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet")},
        cookies=cookies,
    )
    if ok:
        result = r.json()
        # 数据库唯一约束触发：整行被 reject，success=0, failed=1
        has_db_error = result["failed_count"] == 1 and result["success_count"] == 0
        has_unique_violation = any("UniqueViolation" in e.get("reason", "") or "idx_episodes_year_number" in e.get("reason", "") for e in result["errors"])
        print(f"  {'[OK]' if has_unique_violation else '[FAIL]'} 数据库唯一约束触发 (failed_count=1, UniqueViolation in reason)")

    # ---- Step 7: 未知编导软落地 ----
    print("\n=== Step 7: Unknown editor soft-landing ===")
    excel_data = make_excel([
        ["episode_number", "program_name", "air_date", "editor_name",
         "audience_share", "audience_rating", "is_rerun", "notes"],
        [301, "《军事科技》编导软引用测试", "2026-09-01", "李不存在", 0.69, 3.1, "否", ""],
    ])
    r, ok = step(
        "POST with unknown editor_name='李不存在' → 200",
        200,
        "POST",
        f"{BASE}/api/imports/episodes",
        files={"file": ("test_unknown_editor.xlsx", excel_data, "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet")},
        cookies=cookies,
    )
    if ok:
        result = r.json()
        ok = (result["success_count"] == 1 and result["failed_count"] == 0)
        print(f"  {'[OK]' if ok else '[FAIL]'} success_count=1")
    # 验证 editor_id=null：先从列表拿到真实 id，再调用 GET /api/episodes/{id}
    r_list, ok_list = step(
        "GET /api/episodes?limit=100 → 找到刚导入的 episode_number=301",
        200,
        "GET",
        f"{BASE}/api/episodes?limit=100",
        cookies=cookies,
    )
    if ok_list:
        eps = r_list.json()
        ep_301 = next((e for e in eps if e.get("episode_number") == 301), None)
        if ep_301:
            real_id = ep_301["id"]
            r_ep, ok_ep = step(
                f"GET /api/episodes/{real_id} → editor_id=null, editor_name_snapshot='李不存在'",
                200,
                "GET",
                f"{BASE}/api/episodes/{real_id}",
                cookies=cookies,
            )
            if ok_ep:
                ep = r_ep.json()
                editor_id_null = ep.get("editor_id") is None
                name_match = ep.get("editor_name_snapshot") == "李不存在"
                print(f"  {'[OK]' if editor_id_null else '[FAIL]'} editor_id=null ({ep.get('editor_id')})")
                print(f"  {'[OK]' if name_match else '[FAIL]'} editor_name_snapshot='李不存在' ({ep.get('editor_name_snapshot')})")
        else:
            print(f"  [FAIL] episode_number=301 not found in list (import may have failed)")

    print("\n=== All steps done ===")
    print("NOTE: This script is辅助. 制片人仍需在 Swagger UI (http://localhost:8000/docs) 手动跑完 8 步确认全绿.")


if __name__ == "__main__":
    main()