from __future__ import annotations

import time
from datetime import datetime, timedelta

from sqlalchemy import func, select

from app.core.rewards import CN_TZ
from app.core.security import decode_token
from app.db.session import SessionLocal
from app.models.comparison import ComparisonRecord


def _login(client) -> tuple[str, int]:
    phone = f"137{int(time.time() * 1000) % 100000000:08d}"
    sent = client.post("/api/v1/auth/sms/send", json={"phone": phone})
    assert sent.status_code == 200, sent.text
    logged_in = client.post(
        "/api/v1/auth/sms/login",
        json={"phone": phone, "code": "123456"},
    )
    assert logged_in.status_code == 200, logged_in.text
    token = logged_in.json()["access_token"]
    return token, int(decode_token(token, expected_type="access")["sub"])


def _headers(token: str) -> dict[str, str]:
    return {"Authorization": f"Bearer {token}"}


def test_compare_start_requires_login(client) -> None:
    response = client.post(
        "/api/v1/compare/start",
        json={"trace_id": "quota-no-auth", "business_type": "food"},
    )
    assert response.status_code == 401


def test_compare_start_is_idempotent_by_trace_id(client) -> None:
    token, user_id = _login(client)
    payload = {
        "trace_id": f"quota-idempotent-{user_id}",
        "business_type": "ecom",
        "device_id": "quota-device",
    }

    first = client.post("/api/v1/compare/start", json=payload, headers=_headers(token))
    retry = client.post("/api/v1/compare/start", json=payload, headers=_headers(token))

    assert first.status_code == 200, first.text
    assert first.json() == {"limit": 100, "used": 1, "remaining": 99}
    assert retry.status_code == 200, retry.text
    assert retry.json() == first.json()
    with SessionLocal() as db:
        count = db.scalar(
            select(func.count(ComparisonRecord.id)).where(
                ComparisonRecord.trace_id == payload["trace_id"]
            )
        )
        record = db.execute(
            select(ComparisonRecord).where(
                ComparisonRecord.trace_id == payload["trace_id"]
            )
        ).scalar_one()
    assert count == 1
    assert record.user_id == user_id
    assert record.status == "running"
    assert record.business_type == "ecom"
    assert record.device_id == "quota-device"


def test_compare_start_rejects_101st_beijing_day_attempt(client) -> None:
    token, user_id = _login(client)
    now = datetime.now(CN_TZ).replace(tzinfo=None)
    with SessionLocal() as db:
        db.add_all(
            [
                ComparisonRecord(
                    user_id=user_id,
                    trace_id=f"quota-full-{user_id}-{index}",
                    status="failed",
                    created_at=now,
                )
                for index in range(99)
            ]
        )
        db.add(
            ComparisonRecord(
                user_id=user_id,
                trace_id=f"quota-yesterday-{user_id}",
                status="success",
                created_at=now - timedelta(days=1),
            )
        )
        db.commit()

    final_allowed_trace = f"quota-final-allowed-{user_id}"
    allowed = client.post(
        "/api/v1/compare/start",
        json={"trace_id": final_allowed_trace, "business_type": "food"},
        headers=_headers(token),
    )
    assert allowed.status_code == 200, allowed.text
    assert allowed.json() == {"limit": 100, "used": 100, "remaining": 0}

    rejected_trace = f"quota-rejected-{user_id}"
    response = client.post(
        "/api/v1/compare/start",
        json={"trace_id": rejected_trace, "business_type": "food"},
        headers=_headers(token),
    )

    assert response.status_code == 429
    assert response.json()["detail"] == "今日已比价超过100次，请明天再试"
    with SessionLocal() as db:
        assert db.scalar(
            select(func.count(ComparisonRecord.id)).where(
                ComparisonRecord.trace_id == rejected_trace
            )
        ) == 0
