#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""分页接口总数测试脚本
测试接口：
1. /dpc/employee/list - 员工列表（MyBatis Plus分页）
2. /dpc/intermediary/list - 中介黑名单列表（若依startPage分页）
"""

import sys
import io
import requests
import json
from datetime import datetime

# 设置stdout编码为UTF-8
if sys.platform == 'win32':
    sys.stdout = io.TextIOWrapper(sys.stdout.buffer, encoding='utf-8')
    sys.stderr = io.TextIOWrapper(sys.stderr.buffer, encoding='utf-8')

BASE_URL = "http://localhost:8080"
LOGIN_URL = f"{BASE_URL}/login/test"
EMPLOYEE_LIST_URL = f"{BASE_URL}/dpc/employee/list"
INTERMEDIARY_LIST_URL = f"{BASE_URL}/dpc/intermediary/list"

# 测试结果存储
test_results = []


def login():
    """登录获取token"""
    print("=" * 60)
    print("步骤1: 获取认证Token")
    print("=" * 60)

    login_body = {
        "username": "admin",
        "password": "admin123"
    }

    try:
        # 使用json参数发送JSON格式请求体
        response = requests.post(LOGIN_URL, json=login_body)
        print(f"请求URL: {LOGIN_URL}")
        print(f"请求参数: {login_body}")
        print(f"响应状态码: {response.status_code}")

        data = response.json()
        if data.get("code") == 200:
            token = data.get("token")
            print(f"✓ Token获取成功: {token[:20]}...")
            return token
        else:
            print(f"✗ Token获取失败: {data.get('msg')}")
            return None
    except Exception as e:
        print(f"✗ 异常: {e}")
        return None


def test_page(url, token, page_num, page_size, test_name, api_type):
    """测试指定分页参数的接口"""
    print(f"\n========== 测试: {test_name} ==========")
    print(f"API类型: {api_type}")
    print(f"URL: {url}")
    print(f"参数: pageNum={page_num}, pageSize={page_size}")

    params = {
        "pageNum": page_num,
        "pageSize": page_size
    }

    headers = {
        "Authorization": f"Bearer {token}"
    }

    try:
        response = requests.get(url, params=params, headers=headers)
        print(f"响应状态码: {response.status_code}")

        if response.status_code != 200:
            print(f"✗ HTTP错误: {response.text}")
            test_results.append({
                "test_name": test_name,
                "api_type": api_type,
                "status": "FAIL",
                "error": f"HTTP {response.status_code}"
            })
            return None

        data = response.json()
        print(f"响应内容:\n{json.dumps(data, indent=2, ensure_ascii=False)}")

        code = data.get("code", 0)

        if code != 200:
            print(f"✗ 业务错误: {data.get('msg')}")
            test_results.append({
                "test_name": test_name,
                "api_type": api_type,
                "status": "FAIL",
                "error": data.get("msg", "Unknown error")
            })
            return None

        # 检查响应结构 - 支持两种格式
        # 格式1: {data: {total, rows}, code, msg}
        # 格式2: {total, rows, code, msg}
        if "data" in data:
            response_data = data["data"]
            rows = response_data.get("rows", [])
            total = response_data.get("total")
        else:
            # 扁平结构格式
            rows = data.get("rows", [])
            total = data.get("total")

        rows_count = len(rows)
        print(f"\n--- 分页数据分析 ---")
        print(f"返回行数(rows): {rows_count}")
        print(f"总数(total): {total}")

        # 验证total字段
        if total is None:
            print(f"✗ 响应缺少total字段")
            test_results.append({
                "test_name": test_name,
                "api_type": api_type,
                "status": "FAIL",
                "error": "响应缺少total字段"
            })
            return None

        if not isinstance(total, int):
            print(f"✗ total类型错误: {type(total)}")
            test_results.append({
                "test_name": test_name,
                "api_type": api_type,
                "status": "FAIL",
                "error": f"total类型错误: {type(total)}"
            })
            return None

        if total < 0:
            print(f"✗ total值无效: {total}")
            test_results.append({
                "test_name": test_name,
                "api_type": api_type,
                "status": "FAIL",
                "error": f"total值无效: {total}"
            })
            return None

        # 计算预期值
        expected_total_pages = (total + page_size - 1) // page_size
        if page_num < expected_total_pages:
            expected_rows = page_size
        elif page_num == expected_total_pages:
            expected_rows = total - (page_size * (page_num - 1))
        else:
            expected_rows = 0

        print(f"预期行数: {expected_rows}")
        print(f"预期总页数: {expected_total_pages}")

        # 验证行数是否正确
        is_correct = rows_count == expected_rows

        if is_correct:
            print(f"✓ 测试通过 - 分页总数返回正常")
            test_results.append({
                "test_name": test_name,
                "api_type": api_type,
                "status": "PASS",
                "page_num": page_num,
                "page_size": page_size,
                "rows_count": rows_count,
                "total": total,
                "expected_rows": expected_rows
            })
        else:
            print(f"✗ 测试失败 - 预期{expected_rows}行，实际{rows_count}行")
            test_results.append({
                "test_name": test_name,
                "api_type": api_type,
                "status": "FAIL",
                "page_num": page_num,
                "page_size": page_size,
                "rows_count": rows_count,
                "total": total,
                "expected_rows": expected_rows,
                "error": f"行数不匹配"
            })

        # 显示返回的ID
        if rows:
            if "employeeId" in rows[0]:
                ids = ', '.join([str(r.get("employeeId")) for r in rows])
                print(f"员工ID: {ids}")
            elif "intermediaryId" in rows[0]:
                ids = ', '.join([str(r.get("intermediaryId")) for r in rows])
                print(f"中介ID: {ids}")

        return {
            "success": True,
            "rows": rows_count,
            "total": total,
            "expected_rows": expected_rows,
            "is_correct": is_correct
        }

    except Exception as e:
        print(f"✗ 异常: {e}")
        test_results.append({
            "test_name": test_name,
            "api_type": api_type,
            "status": "ERROR",
            "error": str(e)
        })
        return None


def test_consistency(url, token, test_name, api_type):
    """测试不同pageSize下total是否一致"""
    print(f"\n========== 测试总数一致性: {test_name} ==========")

    page_sizes = [10, 20, 50]
    totals = []

    for size in page_sizes:
        params = {"pageNum": 1, "pageSize": size}
        headers = {"Authorization": f"Bearer {token}"}

        try:
            response = requests.get(url, params=params, headers=headers)
            if response.status_code == 200:
                data = response.json()
                if data.get("code") == 200:
                    # 支持两种响应格式
                    if "data" in data:
                        total = data.get("data", {}).get("total")
                    else:
                        total = data.get("total")
                    totals.append(total)
                    print(f"pageSize={size}: total={total}")
        except Exception as e:
            print(f"✗ 异常: {e}")

    if len(set(totals)) == 1 and totals[0] is not None:
        print(f"✓ 不同pageSize下总数一致: {totals[0]}")
        return True
    else:
        print(f"✗ 不同pageSize下总数不一致: {totals}")
        return False


def generate_report():
    """生成测试报告"""
    print("\n" + "=" * 60)
    print("测试报告")
    print("=" * 60)

    # 按API类型分组
    employee_results = [r for r in test_results if "员工" in r.get("test_name", "")]
    intermediary_results = [r for r in test_results if "中介" in r.get("test_name", "")]

    pass_count = sum(1 for r in test_results if r["status"] == "PASS")
    fail_count = sum(1 for r in test_results if r["status"] == "FAIL")
    error_count = sum(1 for r in test_results if r["status"] == "ERROR")

    print(f"\n总测试数: {len(test_results)}")
    print(f"通过: {pass_count}")
    print(f"失败: {fail_count}")
    print(f"错误: {error_count}")

    # 员工接口结果
    print(f"\n--- 员工列表接口 (MyBatis Plus) ---")
    print(f"测试数: {len(employee_results)}")
    for r in employee_results:
        status_icon = "✓" if r["status"] == "PASS" else "✗"
        print(f"{status_icon} {r['test_name']}: {r['status']}")
        if r["status"] == "PASS":
            print(f"   页码: {r.get('page_num')}/{r.get('page_size')}, "
                  f"返回行数: {r.get('rows_count')}, 总数: {r.get('total')}")
        else:
            print(f"   错误: {r.get('error', 'Unknown')}")

    # 中介黑名单接口结果
    print(f"\n--- 中介黑名单接口 (若依startPage) ---")
    print(f"测试数: {len(intermediary_results)}")
    for r in intermediary_results:
        status_icon = "✓" if r["status"] == "PASS" else "✗"
        print(f"{status_icon} {r['test_name']}: {r['status']}")
        if r["status"] == "PASS":
            print(f"   页码: {r.get('page_num')}/{r.get('page_size')}, "
                  f"返回行数: {r.get('rows_count')}, 总数: {r.get('total')}")
        else:
            print(f"   错误: {r.get('error', 'Unknown')}")

    # 总体结论
    print(f"\n--- 测试结论 ---")
    if fail_count == 0 and error_count == 0:
        print("✓ 所有分页接口总数返回正常")
    else:
        print("✗ 存在分页接口总数返回异常")

    # 保存报告到文件
    timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
    report_file = f"test/pagination_test_report_{timestamp}.txt"

    with open(report_file, "w", encoding="utf-8") as f:
        f.write("=" * 60 + "\n")
        f.write("分页接口总数测试报告\n")
        f.write("=" * 60 + "\n\n")
        f.write(f"测试时间: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}\n\n")

        f.write("测试统计:\n")
        f.write(f"  总测试数: {len(test_results)}\n")
        f.write(f"  通过: {pass_count}\n")
        f.write(f"  失败: {fail_count}\n")
        f.write(f"  错误: {error_count}\n\n")

        f.write("测试接口:\n")
        f.write("  1. /dpc/employee/list - 员工列表（MyBatis Plus分页）\n")
        f.write("  2. /dpc/intermediary/list - 中介黑名单列表（若依startPage分页）\n\n")

        f.write("-" * 60 + "\n")
        f.write("详细结果:\n")
        f.write("-" * 60 + "\n\n")

        for r in test_results:
            f.write(f"测试: {r['test_name']}\n")
            f.write(f"API类型: {r['api_type']}\n")
            f.write(f"状态: {r['status']}\n")
            if r['status'] == 'PASS':
                f.write(f"  页码: {r.get('page_num')}/{r.get('page_size')}\n")
                f.write(f"  返回行数: {r.get('rows_count')}\n")
                f.write(f"  总数: {r.get('total')}\n")
                f.write(f"  预期行数: {r.get('expected_rows')}\n")
            else:
                f.write(f"  错误: {r.get('error', 'Unknown')}\n")
            f.write("\n")

        f.write("-" * 60 + "\n")
        f.write("测试结论:\n")
        if fail_count == 0 and error_count == 0:
            f.write("✓ 所有分页接口总数返回正常\n")
        else:
            f.write("✗ 存在分页接口总数返回异常\n")

    print(f"\n报告已保存至: {report_file}")


def main():
    """主函数"""
    print("\n" + "=" * 60)
    print("分页接口总数测试")
    print("=" * 60)
    print(f"测试时间: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}")

    # 获取token
    token = login()
    if not token:
        print("\n✗ 无法获取token，测试终止")
        return

    print("\n✓ 登录成功，开始测试")

    # 员工列表接口测试用例
    employee_test_cases = [
        {"page_num": 1, "page_size": 10, "desc": "员工列表 - 第1页(10条/页)"},
        {"page_num": 2, "page_size": 10, "desc": "员工列表 - 第2页(10条/页)"},
        {"page_num": 1, "page_size": 5, "desc": "员工列表 - 第1页(5条/页)"},
        {"page_num": 1, "page_size": 20, "desc": "员工列表 - 第1页(20条/页)"},
    ]

    print("\n" + "=" * 60)
    print("测试员工列表接口（MyBatis Plus分页）")
    print("=" * 60)

    for test_case in employee_test_cases:
        test_page(
            EMPLOYEE_LIST_URL,
            token,
            test_case["page_num"],
            test_case["page_size"],
            test_case["desc"],
            "MyBatis Plus"
        )

    # 测试总数一致性
    test_consistency(
        EMPLOYEE_LIST_URL,
        token,
        "员工列表-总数一致性",
        "MyBatis Plus"
    )

    # 中介黑名单接口测试用例
    intermediary_test_cases = [
        {"page_num": 1, "page_size": 10, "desc": "中介黑名单 - 第1页(10条/页)"},
        {"page_num": 2, "page_size": 10, "desc": "中介黑名单 - 第2页(10条/页)"},
        {"page_num": 1, "page_size": 5, "desc": "中介黑名单 - 第1页(5条/页)"},
        {"page_num": 1, "page_size": 20, "desc": "中介黑名单 - 第1页(20条/页)"},
    ]

    print("\n" + "=" * 60)
    print("测试中介黑名单接口（若依startPage分页）")
    print("=" * 60)

    for test_case in intermediary_test_cases:
        test_page(
            INTERMEDIARY_LIST_URL,
            token,
            test_case["page_num"],
            test_case["page_size"],
            test_case["desc"],
            "若依startPage"
        )

    # 测试总数一致性
    test_consistency(
        INTERMEDIARY_LIST_URL,
        token,
        "中介黑名单-总数一致性",
        "若依startPage"
    )

    # 生成报告
    generate_report()

    print("\n" + "=" * 60)
    print("测试完成")
    print("=" * 60)


if __name__ == "__main__":
    main()
