"""admin 领券明细逐场点位分数与按需明细。"""
from datetime import UTC, date, datetime

from fastapi.testclient import TestClient
from sqlalchemy import delete

from app.admin.main import admin_app
from app.admin.repositories import admin_user as admin_repo
from app.admin.repositories.coupon_data import (
    _point_scores_by_trace,
    coupon_data_report,
    coupon_point_details,
)
from app.admin.security import create_admin_token
from app.db.session import SessionLocal
from app.models.coupon_state import CouponClaimEvent, CouponSession


def test_point_scores_by_trace() -> None:
    """已领算成功、失败算尝试、跳过不进分母。"""
    db = SessionLocal()
    trace = "point-score-trace"
    try:
        db.add_all([
            CouponClaimEvent(
                trace_id=trace,
                device_id="score-device",
                coupon_id=f"mt-score-{status}",
                claim_date=date(2020, 1, 2),
                status=status,
                coupon_name=f"测试点位-{status}",
                reason="测试失败" if status == "failed" else None,
            )
            for status in ("success", "already_claimed", "failed", "skipped")
        ])
        db.flush()

        stats = _point_scores_by_trace(db, [trace])[trace]
        assert stats["succeeded"] == 2
        assert stats["tried"] == 3
        details = coupon_point_details(db, trace_id=trace)
        assert [item["status"] for item in details] == [
            "success", "already_claimed", "failed", "skipped"
        ]
        assert details[2]["reason"] == "测试失败"
    finally:
        db.rollback()
        db.close()


def test_skipped_detail_does_not_create_a_score() -> None:
    """仅有 skipped 时按需明细仍可查到，但列表没有虚假的 0/0 分数。"""
    db = SessionLocal()
    trace = "point-score-skipped"
    try:
        db.add(CouponClaimEvent(
            trace_id=trace,
            device_id="score-device-skipped",
            coupon_id="mt-score-skipped-only",
            claim_date=date(2020, 1, 2),
            status="skipped",
        ))
        db.flush()

        scores = _point_scores_by_trace(db, [trace, "missing-trace"])
        assert trace not in scores
        assert "missing-trace" not in scores
        assert coupon_point_details(db, trace_id=trace)[0]["status"] == "skipped"
    finally:
        db.rollback()
        db.close()


def test_coupon_data_report_returns_scores_without_embedding_details() -> None:
    """主列表只返回聚合分数，逐券记录必须走按 trace 的明细查询。"""
    db = SessionLocal()
    trace = "point-score-report"
    report_date = date(2020, 1, 4)
    try:
        db.add(CouponSession(
            trace_id=trace,
            device_id="score-report-device",
            status="completed",
            app_env="prod",
            platforms=["meituan-waimai"],
            started_at=datetime(2020, 1, 4, tzinfo=UTC),
            started_date=report_date,
        ))
        db.add_all([
            CouponClaimEvent(
                trace_id=trace,
                device_id="score-report-device",
                coupon_id=f"mt-report-{status}",
                claim_date=report_date,
                status=status,
            )
            for status in ("success", "failed")
        ])
        db.flush()

        report = coupon_data_report(
            db,
            date_from=report_date.isoformat(),
            date_to=report_date.isoformat(),
            app_env="prod",
        )
        row = next(item for item in report["items"] if item["trace_id"] == trace)
        assert row["point_success_count"] == 1
        assert row["point_total_count"] == 2
        assert "point_details" not in row
        assert len(coupon_point_details(db, trace_id=trace)) == 2
    finally:
        db.rollback()
        db.close()


def test_coupon_point_details_endpoint() -> None:
    """前端点击使用的接口按约定返回 trace_id 和逐券 items。"""
    db = SessionLocal()
    trace = "point-details-endpoint"
    try:
        admin = admin_repo.get_by_username(db, "point_details_admin")
        if admin is None:
            admin = admin_repo.create_admin(
                db,
                username="point_details_admin",
                password="pass1234",
                role="super_admin",
            )
        token, _expires_at = create_admin_token(admin_id=admin.id, role=admin.role)
        db.add(CouponClaimEvent(
            trace_id=trace,
            device_id="point-details-endpoint-device",
            coupon_id="mt-point-details-endpoint",
            coupon_name="接口测试券",
            claim_date=date(2020, 1, 5),
            status="failed",
            reason="接口测试失败",
        ))
        db.commit()

        response = TestClient(admin_app).get(
            "/admin/api/coupon-data/point-details",
            params={"trace_id": trace},
            headers={"Authorization": f"Bearer {token}"},
        )
        assert response.status_code == 200, response.text
        assert response.json() == {
            "trace_id": trace,
            "items": [{
                "coupon_id": "mt-point-details-endpoint",
                "coupon_name": "接口测试券",
                "status": "failed",
                "reason": "接口测试失败",
            }],
        }
    finally:
        db.rollback()
        db.execute(delete(CouponClaimEvent).where(CouponClaimEvent.trace_id == trace))
        db.commit()
        db.close()
