"""ad_ecpm_record.trace_id 落库 + 按 trace 聚合广告收益(元)。"""
from __future__ import annotations

from datetime import UTC, date, datetime

from sqlalchemy import delete

from app.db.session import SessionLocal
from app.models.ad_ecpm import AdEcpmRecord
from app.models.coupon_state import CouponSession
from app.repositories import ad_ecpm as crud_ecpm


def _ecpm(trace_id: str, ecpm_raw: str, session_id: str) -> AdEcpmRecord:
    """构造一条 Draw 展示 eCPM(不 commit;ad_session_id 全局唯一,须各不相同)。"""
    return AdEcpmRecord(
        user_id=1,
        ad_type="draw",
        feed_scene="comparison",
        ad_session_id=session_id,
        ecpm_raw=ecpm_raw,
        trace_id=trace_id,
        report_date="2020-01-02",
        created_at=datetime(2020, 1, 2, tzinfo=UTC),
    )


def test_revenue_yuan_by_trace_sums_and_clamps() -> None:
    """同一 trace 多条展示求和;收益=min(eCPM元,¥500)/1000;无展示的 trace 不出现。

    ecpm 200 分→2.0 元/千次→0.002 元/次;300 分→0.003;合计 0.005。
    """
    db = SessionLocal()
    try:
        db.add_all([
            _ecpm("t1", "200", "sess-t1-a"),
            _ecpm("t1", "300", "sess-t1-b"),
            _ecpm("t2", "0", "sess-t2-a"),
            _ecpm("t_cap", "60000", "sess-cap-a"),  # 600 元 CPM > ¥500 钳顶
        ])
        db.flush()
        rev = crud_ecpm.revenue_yuan_by_trace(db, ["t1", "t2", "t3", "t_cap"])
        assert rev["t1"] == 0.005
        assert rev["t2"] == 0.0  # 有展示但 eCPM=0 → 0 元(仍在结果里)
        assert "t3" not in rev  # 无展示的 trace 不出现在结果里
        assert rev["t_cap"] == 0.5  # min(¥600, ¥500)/1000,证明钳顶生效(非 0.6)
    finally:
        db.rollback()
        db.close()


def test_revenue_yuan_by_trace_empty() -> None:
    """空 trace 列表直接返回 {}(避免 IN () 非法)。"""
    db = SessionLocal()
    try:
        assert crud_ecpm.revenue_yuan_by_trace(db, []) == {}
    finally:
        db.close()


def test_short_exposure_keeps_record_with_zero_revenue() -> None:
    """不足一秒仍落展示记录，以便后台显示 0 而不是未填充。"""
    db = SessionLocal()
    try:
        rec = crud_ecpm.create_ecpm_record(
            db, 1, ad_type="draw", ecpm_raw="350",
            ad_session_id="sess-short-exposure", feed_scene="coupon",
            trace_id="trace-short-exposure", exposure_ms=999,
        )
        assert rec.ecpm_raw == "0"
        assert crud_ecpm.revenue_yuan_by_trace(db, ["trace-short-exposure"]) == {
            "trace-short-exposure": 0.0
        }
    finally:
        db.execute(delete(AdEcpmRecord).where(
            AdEcpmRecord.ad_session_id == "sess-short-exposure"
        ))
        db.commit()
        db.close()


def test_one_second_exposure_keeps_original_ecpm() -> None:
    assert crud_ecpm.effective_ecpm_raw("350", 1000) == "350"
    assert crud_ecpm.effective_ecpm_raw("350", None) == "350"


def test_terminal_coupon_trace_is_not_attributable_to_late_impression() -> None:
    """领券失败或被放弃后才到达的广告展示保留收益记录，但不再关联死亡 trace。"""
    db = SessionLocal()
    try:
        db.add_all([
            CouponSession(
                trace_id="failed-before-ad", device_id="d-late-ad", status="failed", app_env="prod",
                started_at=datetime(2020, 1, 2, tzinfo=UTC), started_date=date(2020, 1, 2),
            ),
            CouponSession(
                trace_id="abandoned-before-ad", device_id="d-late-ad", status="abandoned", app_env="prod",
                started_at=datetime(2020, 1, 2, tzinfo=UTC), started_date=date(2020, 1, 2),
            ),
        ])
        db.flush()

        assert crud_ecpm.attributable_trace_id(
            db, feed_scene="coupon", trace_id="failed-before-ad"
        ) is None
        assert crud_ecpm.attributable_trace_id(
            db, feed_scene="coupon", trace_id="abandoned-before-ad"
        ) is None
        assert crud_ecpm.attributable_trace_id(
            db, feed_scene="coupon", trace_id="failed-before-ad", exposure_ms=999
        ) == "failed-before-ad"
        assert crud_ecpm.attributable_trace_id(
            db, feed_scene="coupon", trace_id="abandoned-before-ad", exposure_ms=999
        ) == "abandoned-before-ad"
        assert crud_ecpm.attributable_trace_id(
            db, feed_scene="comparison", trace_id="failed-before-ad"
        ) == "failed-before-ad"
        assert crud_ecpm.attributable_trace_id(
            db, feed_scene="coupon", trace_id="unknown-trace"
        ) == "unknown-trace"
    finally:
        db.rollback()
        db.close()


def test_create_ecpm_record_persists_trace_id() -> None:
    """create_ecpm_record 落 trace_id。"""
    db = SessionLocal()
    try:
        rec = crud_ecpm.create_ecpm_record(
            db, 1, ad_type="draw", ecpm_raw="150",
            ad_session_id="sess-trace-persist", feed_scene="coupon",
            trace_id="trace-xyz",
        )
        assert rec.trace_id == "trace-xyz"
    finally:
        db.execute(delete(AdEcpmRecord).where(AdEcpmRecord.ad_session_id == "sess-trace-persist"))
        db.commit()
        db.close()
