#!/usr/bin/env python3
import concurrent.futures
import json
import pathlib
import statistics
import time
from datetime import datetime, timedelta

import requests

ROOT = pathlib.Path(__file__).resolve().parents[1]
API_DIR = ROOT / "api"
OLD_DATA = ROOT.parent / "20260622-full-retest" / "data"
BASE = "http://8.147.119.215:8080"
SESSION = requests.Session()
SESSION.trust_env = False
RUN_ID = datetime.now().strftime("%H%M%S")


def post(path, data=None, timeout=60):
    started = time.time()
    try:
        resp = SESSION.post(BASE + path, data=data or {}, timeout=timeout)
        elapsed = time.time() - started
        try:
            body = resp.json()
        except Exception:
            body = {"code": 1, "message": resp.text[:1000]}
        return {"status": resp.status_code, "elapsed": elapsed, "body": body}
    except Exception as exc:
        return {"status": 0, "elapsed": time.time() - started, "body": {"code": 1, "message": repr(exc)}}


def load_tsv(path):
    rows = []
    with open(path, encoding="utf-8") as f:
        for line in f:
            line = line.rstrip("\n")
            if line:
                rows.append(line.split("\t"))
    return rows


def user_10_station_flow(idx, staff_rows, dishes, plate_prefix="MS"):
    staff_uuid, staff_name, mobile = staff_rows[idx - 1]
    plate = f"CSFZX_{plate_prefix}{RUN_ID}_{idx:04d}"
    # 第一个称重台前完成刷脸绑盘，后续 10 台称重台复用同一个餐盘。
    first_equipment = f"CSFZX460{((idx - 1) % 60) + 1:03d}"
    bind = post(
        "/p/api/bindPlate",
        {
            "staff_uuid": staff_uuid,
            "plate_code": plate,
            "equipment_code": first_equipment,
            "card_id": f"CSFZX_CARD_{idx:04d}",
        },
        timeout=60,
    )
    pushes = []
    if not (bind["status"] == 200 and bind["body"].get("code") == 0):
        return {
            "idx": idx,
            "staff_uuid": staff_uuid,
            "staff_name": staff_name,
            "mobile": mobile,
            "plate": plate,
            "bind": bind,
            "pushes": pushes,
            "ok": False,
            "elapsed": bind["elapsed"],
        }
    for offset in range(10):
        equipment_no = ((idx - 1 + offset) % 60) + 1
        code = f"CSFZX460{equipment_no:03d}"
        dish_uuid, dish_name, weight, price = dishes[(idx - 1 + offset) % len(dishes)]
        create_date = (datetime.now() + timedelta(seconds=5 + offset)).strftime("%Y-%m-%d %H:%M:%S")
        dish_info = {
            "create_date": create_date,
            "details": [{"uuid": dish_uuid, "weight": float(weight)}],
        }
        push = post(
            "/p/api/zhctPushMeal",
            {
                "code": plate,
                "equipment_code": code,
                "dish_info": json.dumps(dish_info, ensure_ascii=False),
            },
            timeout=90,
        )
        pushes.append({
            "equipment_code": code,
            "dish_uuid": dish_uuid,
            "dish_name": dish_name,
            "push": push,
        })
    ok = bind["status"] == 200 and bind["body"].get("code") == 0 and all(
        row["push"]["status"] == 200 and row["push"]["body"].get("code") == 0 for row in pushes
    )
    return {
        "idx": idx,
        "staff_uuid": staff_uuid,
        "staff_name": staff_name,
        "mobile": mobile,
        "plate": plate,
        "bind": bind,
        "pushes": pushes,
        "ok": ok,
        "elapsed": bind["elapsed"] + sum(row["push"]["elapsed"] for row in pushes),
    }


def main():
    staff_rows = load_tsv(OLD_DATA / "csfzx_load_staff_1500.tsv")
    dishes = load_tsv(OLD_DATA / "csfzx_load_dishes_60.tsv")
    sample = [user_10_station_flow(i, staff_rows, dishes, "SM") for i in range(1, 4)]
    (API_DIR / "50-sample-3-user-10-station-flow.json").write_text(json.dumps(sample, ensure_ascii=False, indent=2))

    started = time.time()
    results = []
    with concurrent.futures.ThreadPoolExecutor(max_workers=60) as pool:
        futures = [pool.submit(user_10_station_flow, i, staff_rows, dishes, "LD") for i in range(1, 1501)]
        for fut in concurrent.futures.as_completed(futures):
            results.append(fut.result())

    elapsed = time.time() - started
    latencies = [row["elapsed"] for row in results]
    latencies_sorted = sorted(latencies)
    summary = {
        "scenario": "1500 users, each user binds once and takes food at 10 different 460 weighing scales",
        "total_users": len(results),
        "bind_requests": len(results),
        "weigh_push_requests": len(results) * 10,
        "expected_initial_menu_rows": len(results) * 10,
        "ok_users": sum(1 for row in results if row["ok"]),
        "fail_users": sum(1 for row in results if not row["ok"]),
        "elapsed_sec": elapsed,
        "user_throughput_per_sec": len(results) / elapsed if elapsed else 0,
        "latency_avg_per_user": statistics.mean(latencies) if latencies else 0,
        "latency_p50_per_user": statistics.median(latencies) if latencies else 0,
        "latency_p95_per_user": latencies_sorted[int(len(latencies_sorted) * 0.95) - 1] if latencies_sorted else 0,
        "latency_max_per_user": max(latencies) if latencies else 0,
    }
    (API_DIR / "51-load-1500-user-10-station-flow-summary.json").write_text(json.dumps(summary, ensure_ascii=False, indent=2))
    (API_DIR / "51-load-1500-user-10-station-flow-full.json").write_text(json.dumps({"summary": summary, "results": results}, ensure_ascii=False, indent=2))
    print(json.dumps(summary, ensure_ascii=False, indent=2))


if __name__ == "__main__":
    main()
