"""
Stress Test Route
Bắn N request đồng thời vào chatbot để test performance/load.
"""

import asyncio
import logging
import time

import httpx
from fastapi import APIRouter
from fastapi.responses import JSONResponse
from pydantic import BaseModel

logger = logging.getLogger(__name__)
router = APIRouter(prefix="/api/stress-test", tags=["Stress Test"])


class StressTestRequest(BaseModel):
    target_url: str
    questions: list[str]  # Test questions to cycle through
    concurrent: int = 5   # Number of concurrent requests
    total: int = 20       # Total number of requests
    timeout: int = 60


async def _send_one(client: httpx.AsyncClient, url: str, question: str, req_id: int) -> dict:
    """Send one request and measure latency."""
    start = time.perf_counter()
    try:
        resp = await client.post(
            url,
            json={"user_query": question},
            headers={"Content-Type": "application/json"},
        )
        elapsed = (time.perf_counter() - start) * 1000  # ms
        resp.raise_for_status()
        data = resp.json()
        return {
            "id": req_id,
            "status": "success",
            "question": question,
            "response_preview": (data.get("ai_response", "") or "")[:200],
            "latency_ms": round(elapsed),
            "status_code": resp.status_code,
        }
    except httpx.HTTPStatusError as e:
        elapsed = (time.perf_counter() - start) * 1000
        return {
            "id": req_id, "status": "error", "question": question,
            "response_preview": f"HTTP {e.response.status_code}",
            "latency_ms": round(elapsed), "status_code": e.response.status_code,
        }
    except Exception as e:
        elapsed = (time.perf_counter() - start) * 1000
        return {
            "id": req_id, "status": "error", "question": question,
            "response_preview": str(e)[:200],
            "latency_ms": round(elapsed), "status_code": 0,
        }


@router.post("/run", summary="Run stress test with concurrent requests")
async def run_stress_test(req: StressTestRequest):
    """
    Bắn total requests với concurrent đồng thời.
    Cycle qua danh sách questions.
    """
    if req.concurrent < 1:
        req.concurrent = 1
    if req.total < 1:
        req.total = 1
    if not req.questions:
        return JSONResponse(status_code=400, content={"status": "error", "message": "No questions"})

    all_results = []
    semaphore = asyncio.Semaphore(req.concurrent)
    overall_start = time.perf_counter()

    async def limited_send(client, url, question, req_id):
        async with semaphore:
            result = await _send_one(client, url, question, req_id)
            all_results.append(result)
            return result

    async with httpx.AsyncClient(timeout=req.timeout) as client:
        tasks = []
        for i in range(req.total):
            q = req.questions[i % len(req.questions)]
            tasks.append(limited_send(client, req.target_url, q, i + 1))
        await asyncio.gather(*tasks, return_exceptions=True)

    overall_elapsed = (time.perf_counter() - overall_start) * 1000

    # Sort by id
    all_results.sort(key=lambda r: r.get("id", 0))

    # Calculate stats
    latencies = [r["latency_ms"] for r in all_results if r.get("latency_ms")]
    ok_count = sum(1 for r in all_results if r["status"] == "success")
    err_count = sum(1 for r in all_results if r["status"] == "error")

    stats = {
        "total": len(all_results),
        "success": ok_count,
        "errors": err_count,
        "error_rate": round(err_count / max(len(all_results), 1) * 100, 1),
        "total_time_ms": round(overall_elapsed),
        "rps": round(len(all_results) / max(overall_elapsed / 1000, 0.001), 1),
    }

    if latencies:
        latencies_sorted = sorted(latencies)
        stats.update({
            "avg_ms": round(sum(latencies) / len(latencies)),
            "min_ms": min(latencies),
            "max_ms": max(latencies),
            "p50_ms": latencies_sorted[len(latencies_sorted) // 2],
            "p95_ms": latencies_sorted[int(len(latencies_sorted) * 0.95)],
            "p99_ms": latencies_sorted[int(len(latencies_sorted) * 0.99)],
        })

    return {
        "status": "success",
        "stats": stats,
        "results": all_results,
    }
