"""
学生端全功能Web测试脚本
使用 Playwright 对学生端所有页面进行功能测试
"""
import json
import os
from datetime import datetime
from playwright.sync_api import sync_playwright, TimeoutError as PlaywrightTimeout

BASE_URL = "http://localhost:3000"
STUDENT_EMAIL = "student_g1c1_1@xiaoxue.edu.cn"
STUDENT_PASSWORD = "123456"

# 学生端所有路由（来自 src/modules/layout/config/navigation.ts 及 src/app/(dashboard)/student/ 目录）
STUDENT_ROUTES = {
    "Dashboard": ["/student/dashboard"],
    "My Learning - Courses": ["/student/learning/courses"],
    "My Learning - Assignments": ["/student/learning/assignments"],
    "My Learning - Textbooks": ["/student/learning/textbooks"],
    "Schedule": ["/student/schedule"],
    "My Grades": ["/student/grades"],
    "Attendance": ["/student/attendance"],
    "Diagnostic": ["/student/diagnostic"],
    "Electives": ["/student/elective"],
    "Announcements": ["/announcements"],
    "Messages": [
        "/messages",
        "/messages/compose",
    ],
    "Profile": ["/profile"],
    "Settings": [
        "/settings",
        "/settings/security",
    ],
    "Common Dashboard": ["/dashboard"],
}

# 详情页发现规则：从列表页提取详情链接
DETAIL_DISCOVERIES = {
    "Assignment Detail": ("/student/learning/assignments", "/student/learning/assignments/"),
    "Textbook Detail": ("/student/learning/textbooks", "/student/learning/textbooks/"),
}

results = {
    "test_date": datetime.now().strftime("%Y-%m-%d %H:%M:%S"),
    "test_target": "学生端 (Student)",
    "base_url": BASE_URL,
    "student_email": STUDENT_EMAIL,
    "summary": {
        "total": 0,
        "passed": 0,
        "failed": 0,
        "warnings": 0,
    },
    "pages": {},
    "console_errors": [],
    "navigation_issues": [],
}


def login(page):
    """登录学生账号"""
    print("\n>>> 登录学生账号...")
    page.goto(f"{BASE_URL}/login", timeout=30000)
    page.wait_for_load_state("networkidle", timeout=15000)

    # 检查是否已登录
    if "/student" in page.url or page.url.rstrip("/").endswith("/dashboard"):
        print("    已登录，跳过登录步骤")
        return True

    try:
        email_input = page.locator('input[name="email"]')
        if email_input.count() == 0:
            email_input = page.locator('input[type="email"]')
        email_input.fill(STUDENT_EMAIL)

        password_input = page.locator('input[name="password"]')
        if password_input.count() == 0:
            password_input = page.locator('input[type="password"]')
        password_input.fill(STUDENT_PASSWORD)

        # 点击登录按钮
        login_btn = page.locator('button[type="submit"]')
        if login_btn.count() == 0:
            login_btn = page.get_by_role("button", name="Sign In with Email")
        if login_btn.count() == 0:
            login_btn = page.locator("button").filter(has_text="Sign In")
        if login_btn.count() == 0:
            login_btn = page.locator("button").filter(has_text="登录")
        login_btn.click()

        page.wait_for_load_state("networkidle", timeout=15000)
        page.wait_for_timeout(2000)

        print(f"    登录后 URL: {page.url}")
        # 验证登录成功
        if "/login" in page.url:
            print("    ❌ 登录后仍在登录页")
            return False
        return True
    except Exception as e:
        print(f"    ❌ 登录失败: {e}")
        return False


def test_page(page, route, category):
    """测试单个页面"""
    url = f"{BASE_URL}{route}"
    page_result = {
        "url": url,
        "category": category,
        "status": "unknown",
        "http_status": None,
        "redirect_url": None,
        "final_url": None,
        "errors": [],
        "warnings": [],
        "title": None,
        "content_length": 0,
    }

    print(f"\n  测试: {category} - {route}")

    # 收集控制台错误
    console_errors = []

    def on_console(msg):
        if msg.type == "error":
            console_errors.append(msg.text)

    page.on("console", on_console)

    # 收集页面错误
    def on_page_error(err):
        console_errors.append(f"[PAGE_ERROR] {err}")

    page.on("pageerror", on_page_error)

    try:
        response = page.goto(url, timeout=30000)
        page.wait_for_load_state("networkidle", timeout=15000)
        page.wait_for_timeout(1000)

        http_status = response.status if response else None
        final_url = page.url

        page_result["http_status"] = http_status
        page_result["final_url"] = final_url

        # 获取页面标题
        try:
            page_result["title"] = page.title()
        except Exception:
            pass

        # 检查是否重定向
        if final_url.rstrip("/") != url.rstrip("/"):
            page_result["redirect_url"] = final_url

        # 检查 HTTP 状态
        if http_status and http_status >= 500:
            page_result["status"] = "failed"
            page_result["errors"].append(f"HTTP {http_status} 服务器错误")
        elif http_status and http_status >= 400:
            page_result["status"] = "warning"
            page_result["warnings"].append(f"HTTP {http_status} 客户端错误")
        elif final_url and "/login" in final_url:
            page_result["status"] = "failed"
            page_result["errors"].append("重定向到登录页 - 认证失败")
        elif final_url and "/500" in final_url:
            page_result["status"] = "failed"
            page_result["errors"].append("重定向到 500 错误页")
        elif final_url and "/404" in final_url:
            page_result["status"] = "warning"
            page_result["warnings"].append("重定向到 404 页面")
        else:
            page_result["status"] = "passed"

        # 检查页面是否有错误提示
        try:
            error_elements = page.locator('[role="alert"], .error, .text-destructive, .text-red-500').all()
            for et in error_elements[:3]:
                try:
                    text = et.text_content()
                    if text and text.strip():
                        page_result["warnings"].append(f"页面错误提示: {text.strip()[:150]}")
                except Exception:
                    pass
        except Exception:
            pass

        # 检查页面是否为空（无主要内容）
        body_text = page.locator("body").text_content() or ""
        page_result["content_length"] = len(body_text.strip())
        if len(body_text.strip()) < 50:
            page_result["warnings"].append("页面内容过少，可能为空")

        # 收集控制台错误
        if console_errors:
            page_result["errors"].extend(console_errors[:5])

        # 打印状态
        status_icon = "✅" if page_result["status"] == "passed" else "⚠️" if page_result["status"] == "warning" else "❌"
        print(f"    {status_icon} {page_result['status']} (HTTP {http_status})")

    except PlaywrightTimeout:
        page_result["status"] = "failed"
        page_result["errors"].append("页面加载超时 (30s)")
        print(f"    ❌ TIMEOUT")
    except Exception as e:
        page_result["status"] = "failed"
        page_result["errors"].append(str(e)[:200])
        print(f"    ❌ ERROR: {str(e)[:100]}")

    finally:
        try:
            page.remove_listener("console", on_console)
            page.remove_listener("pageerror", on_page_error)
        except Exception:
            pass

    return page_result


def discover_detail_links(page, list_route, link_pattern):
    """从列表页发现详情页链接"""
    try:
        url = f"{BASE_URL}{list_route}"
        page.goto(url, timeout=30000)
        page.wait_for_load_state("networkidle", timeout=15000)
        page.wait_for_timeout(1000)

        # 查找匹配的链接
        links = page.locator(f'a[href*="{link_pattern}"]').all()
        detail_urls = []
        seen = set()
        for link in links[:5]:  # 最多取5个
            href = link.get_attribute("href")
            if href and link_pattern in href and href != list_route:
                # 标准化URL
                if not href.startswith("/"):
                    href = "/" + href
                # 排除列表页本身
                if href.rstrip("/") == list_route.rstrip("/"):
                    continue
                if href not in seen:
                    seen.add(href)
                    detail_urls.append(href)
        return detail_urls
    except Exception as e:
        print(f"    发现详情页链接失败: {e}")
        return []


def run_all_tests():
    """运行所有测试"""
    global results

    with sync_playwright() as p:
        browser = p.chromium.launch(headless=True)
        context = browser.new_context(
            viewport={"width": 1440, "height": 900},
            locale="zh-CN",
            ignore_https_errors=True,
        )
        page = context.new_page()

        # 登录
        if not login(page):
            print("❌ 登录失败，终止测试")
            browser.close()
            return

        # 测试所有路由
        total = 0
        for category, routes in STUDENT_ROUTES.items():
            for route in routes:
                page_result = test_page(page, route, category)
                key = route.replace("/", "_").strip("_")
                results["pages"][key] = page_result
                total += 1

        # 发现并测试详情页
        print("\n\n>>> 发现详情页链接...")
        for name, (list_url, pattern) in DETAIL_DISCOVERIES.items():
            detail_urls = discover_detail_links(page, list_url, pattern)
            if detail_urls:
                print(f"\n  {name} 详情页: {len(detail_urls)} 个")
                for detail_url in detail_urls[:2]:
                    route = detail_url
                    if not route.startswith("/"):
                        route = "/" + route
                    page_result = test_page(page, route, f"{name}")
                    key = route.replace("/", "_").strip("_")
                    results["pages"][key] = page_result
                    total += 1
            else:
                print(f"\n  {name}: 未发现详情页链接")

        # 汇总
        passed = sum(1 for pg in results["pages"].values() if pg["status"] == "passed")
        failed = sum(1 for pg in results["pages"].values() if pg["status"] == "failed")
        warnings = sum(1 for pg in results["pages"].values() if pg["status"] == "warning")

        results["summary"]["total"] = total
        results["summary"]["passed"] = passed
        results["summary"]["failed"] = failed
        results["summary"]["warnings"] = warnings

        print(f"\n\n{'='*60}")
        print(f"测试完成: 总计 {total}, 通过 {passed}, 失败 {failed}, 警告 {warnings}")
        print(f"{'='*60}")

        browser.close()


def generate_report():
    """生成Markdown测试报告"""
    report_lines = []
    report_lines.append("# 学生端 Web 功能测试报告")
    report_lines.append("")
    report_lines.append(f"> 测试日期：{results['test_date']}")
    report_lines.append(f"> 测试范围：所有学生端页面功能")
    report_lines.append(f"> 测试工具：Playwright + Chromium (headless)")
    report_lines.append(f"> 测试账号：{results['student_email']}")
    report_lines.append(f"> Base URL：{results['base_url']}")
    report_lines.append("")
    report_lines.append("---")
    report_lines.append("")
    report_lines.append("## 一、测试概览")
    report_lines.append("")
    s = results["summary"]
    report_lines.append("| 指标 | 数值 |")
    report_lines.append("|------|------|")
    report_lines.append(f"| 总测试页面数 | {s['total']} |")
    report_lines.append(f"| 通过 | {s['passed']} |")
    report_lines.append(f"| 失败 | {s['failed']} |")
    report_lines.append(f"| 警告 | {s['warnings']} |")
    report_lines.append(f"| 通过率 | {s['passed']/s['total']*100:.1f}% |" if s['total'] > 0 else "| 通过率 | N/A |")
    report_lines.append("")
    report_lines.append("---")
    report_lines.append("")
    report_lines.append("## 二、页面测试详情")
    report_lines.append("")

    # 按类别分组
    by_category = {}
    for key, page_result in results["pages"].items():
        cat = page_result.get("category", "Other")
        by_category.setdefault(cat, []).append(page_result)

    for category in sorted(by_category.keys()):
        pages = by_category[category]
        report_lines.append(f"### {category}")
        report_lines.append("")
        report_lines.append("| 状态 | URL | HTTP状态 | 结果 | 备注 |")
        report_lines.append("|------|-----|----------|------|------|")

        for pg in pages:
            status_icon = "✅" if pg["status"] == "passed" else "⚠️" if pg["status"] == "warning" else "❌"
            notes = []
            if pg.get("redirect_url"):
                notes.append(f"重定向到: `{pg['redirect_url']}`")
            if pg.get("errors"):
                # 截断并替换换行符，避免破坏 markdown 表格
                err_short = "; ".join(pg["errors"][:2]).replace("\n", " ").replace("\r", " ")
                if len(err_short) > 200:
                    err_short = err_short[:200] + "..."
                notes.append(f"错误: {err_short}")
            if pg.get("warnings"):
                warn_short = "; ".join(pg["warnings"][:2]).replace("\n", " ").replace("\r", " ")
                if len(warn_short) > 200:
                    warn_short = warn_short[:200] + "..."
                notes.append(f"警告: {warn_short}")
            note_str = "<br>".join(notes) if notes else "-"

            # 简化URL显示
            short_url = pg["url"].replace(BASE_URL, "")
            report_lines.append(
                f"| {status_icon} | `{short_url}` | {pg['http_status'] or '-'} | {pg['status']} | {note_str} |"
            )

        report_lines.append("")

    # 失败页面汇总
    failed_pages = [(k, v) for k, v in results["pages"].items() if v["status"] == "failed"]
    if failed_pages:
        report_lines.append("---")
        report_lines.append("")
        report_lines.append("## 三、失败页面详情")
        report_lines.append("")
        for key, pg in failed_pages:
            report_lines.append(f"### ❌ `{pg['url']}`")
            report_lines.append("")
            report_lines.append(f"- **分类**: {pg['category']}")
            report_lines.append(f"- **HTTP状态**: {pg['http_status']}")
            if pg.get("redirect_url"):
                report_lines.append(f"- **重定向**: `{pg['redirect_url']}`")
            if pg.get("errors"):
                report_lines.append(f"- **错误信息**:")
                for err in pg["errors"]:
                    report_lines.append(f"  - {err}")
            report_lines.append("")

    # 警告页面
    warning_pages = [(k, v) for k, v in results["pages"].items() if v["status"] == "warning"]
    next_section_num = 4
    if warning_pages:
        report_lines.append("---")
        report_lines.append("")
        report_lines.append(f"## 四、警告页面")
        report_lines.append("")
        for key, pg in warning_pages:
            report_lines.append(f"### ⚠️ `{pg['url']}`")
            report_lines.append("")
            report_lines.append(f"- **分类**: {pg['category']}")
            if pg.get("warnings"):
                for w in pg["warnings"]:
                    report_lines.append(f"  - {w}")
            report_lines.append("")
        next_section_num = 5

    report_lines.append("---")
    report_lines.append("")
    report_lines.append(f"## {'四' if next_section_num == 4 else '五'}、发现的问题分析")
    report_lines.append("")
    report_lines.append("根据测试结果，发现以下问题：")
    report_lines.append("")

    # 收集所有有错误的页面（包括 passed 但有错误的）
    issue_num = 1
    for key, pg in results["pages"].items():
        if pg.get("errors"):
            report_lines.append(f"### 问题 {issue_num}: {pg['category']} - `{pg['url'].replace(BASE_URL, '')}`")
            report_lines.append("")
            report_lines.append(f"- **状态**: {pg['status']} (HTTP {pg['http_status']})")
            report_lines.append(f"- **错误信息**:")
            for err in pg["errors"]:
                # 截断过长的错误信息
                err_clean = err.replace("\n", " ").replace("\r", " ")
                if len(err_clean) > 300:
                    err_clean = err_clean[:300] + "..."
                report_lines.append(f"  - {err_clean}")
            report_lines.append("")
            issue_num += 1

    report_lines.append("---")
    report_lines.append("")
    report_lines.append(f"## {'五' if next_section_num == 4 else '六'}、测试覆盖范围")
    report_lines.append("")
    report_lines.append("本次测试覆盖学生端以下功能模块：")
    report_lines.append("")
    report_lines.append("| 模块 | 路由 | 说明 |")
    report_lines.append("|------|------|------|")
    report_lines.append("| Dashboard | `/student/dashboard` | 学生仪表盘 |")
    report_lines.append("| My Learning - Courses | `/student/learning/courses` | 我的课程 |")
    report_lines.append("| My Learning - Assignments | `/student/learning/assignments` | 作业列表 |")
    report_lines.append("| My Learning - Assignment Detail | `/student/learning/assignments/[id]` | 作业详情/作答 |")
    report_lines.append("| My Learning - Textbooks | `/student/learning/textbooks` | 教材列表 |")
    report_lines.append("| My Learning - Textbook Detail | `/student/learning/textbooks/[id]` | 教材阅读 |")
    report_lines.append("| Schedule | `/student/schedule` | 课表 |")
    report_lines.append("| My Grades | `/student/grades` | 我的成绩 |")
    report_lines.append("| Attendance | `/student/attendance` | 考勤 |")
    report_lines.append("| Diagnostic | `/student/diagnostic` | 学情诊断 |")
    report_lines.append("| Electives | `/student/elective` | 选课中心 |")
    report_lines.append("| Announcements | `/announcements` | 公告 |")
    report_lines.append("| Messages | `/messages` | 消息列表 |")
    report_lines.append("| Messages - Compose | `/messages/compose` | 写消息 |")
    report_lines.append("| Profile | `/profile` | 个人资料 |")
    report_lines.append("| Settings | `/settings` | 设置 |")
    report_lines.append("| Settings - Security | `/settings/security` | 安全设置 |")
    report_lines.append("| Common Dashboard | `/dashboard` | 通用仪表盘（角色跳转） |")
    report_lines.append("")
    report_lines.append("---")
    report_lines.append("")
    report_lines.append(f"*报告自动生成于 {results['test_date']}*")

    return "\n".join(report_lines)


def main():
    # 运行测试
    run_all_tests()

    # 生成报告
    report = generate_report()

    # 写入文件
    output_dir = os.path.join(os.path.dirname(__file__), "..", "..", "bugs")
    os.makedirs(output_dir, exist_ok=True)
    output_path = os.path.join(output_dir, "student_web_test.md")

    with open(output_path, "w", encoding="utf-8") as f:
        f.write(report)

    print(f"\n📄 报告已写入: {output_path}")

    # 同时输出JSON便于调试
    json_path = output_path.replace(".md", ".json")
    with open(json_path, "w", encoding="utf-8") as f:
        json.dump(results, f, ensure_ascii=False, indent=2, default=str)
    print(f"📄 JSON数据已写入: {json_path}")


if __name__ == "__main__":
    main()
