"""广告收益报表的环境与业务代码位口径。"""
from __future__ import annotations

from datetime import UTC, datetime

from sqlalchemy import delete

from app.admin.repositories import ad_revenue
from app.db.session import SessionLocal
from app.models.ad_ecpm import AdEcpmRecord
from app.models.ad_feed_reward import AdFeedRewardRecord
from app.models.ad_pangle_revenue import AdPangleDailyRevenue
from app.models.ad_reward import AdRewardRecord
from app.models.user import User

REPORT_DATE = "2040-02-03"
PLAYBACK_DATE = "2040-02-04"


def test_business_scope_filters_client_and_pangle_by_env_and_code(monkeypatch) -> None:
    db = SessionLocal()
    try:
        user = User(
            phone="18800009991",
            username="29999999991",
            register_channel="sms",
        )
        db.add(user)
        db.flush()
        db.add_all([
            AdEcpmRecord(
                user_id=user.id, ad_type="reward_video", ad_session_id="scope-prod-business",
                app_env="prod", our_code_id="prod-reward", ecpm_raw="10000",
                report_date=REPORT_DATE, created_at=datetime(2040, 2, 3, tzinfo=UTC),
            ),
            AdEcpmRecord(
                user_id=user.id, ad_type="draw", ad_session_id="scope-prod-demo",
                app_env="prod", our_code_id="prod-demo", ecpm_raw="20000",
                report_date=REPORT_DATE, created_at=datetime(2040, 2, 3, tzinfo=UTC),
            ),
            AdEcpmRecord(
                user_id=user.id, ad_type="draw", ad_session_id="scope-prod-known-business",
                app_env="prod", our_code_id="104098712", ecpm_raw="40000",
                report_date=REPORT_DATE, created_at=datetime(2040, 2, 3, tzinfo=UTC),
            ),
            AdEcpmRecord(
                user_id=user.id, ad_type="reward_video", ad_session_id="scope-test-business",
                app_env="test", our_code_id="104127529", ecpm_raw="30000",
                report_date=REPORT_DATE, created_at=datetime(2040, 2, 3, tzinfo=UTC),
            ),
            AdPangleDailyRevenue(
                report_date=REPORT_DATE, app_env="prod", our_code_id="prod-reward",
                adn="", revenue_yuan=1.5, api_revenue_yuan=1.2, impressions=10,
            ),
            AdPangleDailyRevenue(
                report_date=REPORT_DATE, app_env="prod", our_code_id="prod-demo",
                adn="", revenue_yuan=8.0, api_revenue_yuan=7.0, impressions=40,
            ),
            AdPangleDailyRevenue(
                report_date=REPORT_DATE, app_env="prod", our_code_id="104098712",
                adn="", revenue_yuan=2.5, api_revenue_yuan=2.0, impressions=20,
            ),
            AdPangleDailyRevenue(
                report_date=REPORT_DATE, app_env="test", our_code_id="104127529",
                adn="", revenue_yuan=9.0, api_revenue_yuan=8.0, impressions=50,
            ),
        ])
        db.commit()
        monkeypatch.setattr(
            ad_revenue.app_config,
            "get_ad_config",
            lambda _db: {
                "reward_code_id": "prod-reward",
                "compare_draw_code_id": "prod-draw",
                "coupon_draw_code_id": "prod-draw",
            },
        )

        business = ad_revenue.ad_revenue_report(
            db,
            date_from=REPORT_DATE,
            date_to=REPORT_DATE,
            app_env="prod",
            revenue_scope="business",
        )
        assert business["total_impressions"] == 2
        assert business["total_revenue_yuan"] == 0.5
        assert business["total_pangle_revenue_yuan"] == 4.0
        assert business["total_pangle_api_revenue_yuan"] == 3.2

        all_codes = ad_revenue.ad_revenue_report(
            db,
            date_from=REPORT_DATE,
            date_to=REPORT_DATE,
            app_env="prod",
            revenue_scope="all",
        )
        assert all_codes["total_impressions"] == 3
        assert all_codes["total_revenue_yuan"] == 0.7
        assert all_codes["total_pangle_revenue_yuan"] == 12.0
        assert all_codes["total_pangle_api_revenue_yuan"] == 10.2

        test_business = ad_revenue.ad_revenue_report(
            db,
            date_from=REPORT_DATE,
            date_to=REPORT_DATE,
            app_env="test",
            revenue_scope="business",
        )
        assert test_business["total_impressions"] == 1
        assert test_business["total_revenue_yuan"] == 0.3
        assert test_business["total_pangle_revenue_yuan"] == 9.0
        assert test_business["total_pangle_api_revenue_yuan"] == 8.0

        all_env_business = ad_revenue.ad_revenue_report(
            db,
            date_from=REPORT_DATE,
            date_to=REPORT_DATE,
            app_env=None,
            revenue_scope="business",
        )
        assert all_env_business["total_impressions"] == 3
        assert all_env_business["total_revenue_yuan"] == 0.8
        assert all_env_business["total_pangle_revenue_yuan"] == 13.0
        assert all_env_business["total_pangle_api_revenue_yuan"] == 11.2
    finally:
        db.rollback()
        db.execute(delete(AdPangleDailyRevenue).where(AdPangleDailyRevenue.report_date == REPORT_DATE))
        db.execute(delete(AdEcpmRecord).where(AdEcpmRecord.report_date == REPORT_DATE))
        db.execute(delete(User).where(User.phone == "18800009991"))
        db.commit()
        db.close()


def test_reward_video_incomplete_playback_has_zero_revenue() -> None:
    db = SessionLocal()
    phone = "18800009992"
    sessions = {
        "closed_early": "rv-zero-closed",
        "too_short": "rv-zero-short",
        "capped": "rv-keep-capped",
        "granted": "rv-keep-granted",
    }
    try:
        user = User(phone=phone, username="29999999992", register_channel="sms")
        db.add(user)
        db.flush()

        for index, (status, session_id) in enumerate(sessions.items(), start=1):
            created_at = datetime(2040, 2, 4, index, tzinfo=UTC)
            db.add(AdEcpmRecord(
                user_id=user.id,
                ad_type="reward_video",
                ad_session_id=session_id,
                app_env="prod",
                our_code_id="prod-reward",
                ecpm_raw="10000",
                report_date=PLAYBACK_DATE,
                created_at=created_at,
            ))
            db.add(AdRewardRecord(
                trans_id=f"{session_id}-trans",
                user_id=user.id,
                coin=0,
                status=status,
                reward_scene="reward_video",
                ad_session_id=session_id,
                app_env="prod",
                our_code_id="prod-reward",
                ecpm_raw="10000",
                reward_date=PLAYBACK_DATE,
                created_at=created_at,
            ))
        db.commit()

        result = ad_revenue.ad_revenue_report(
            db,
            date_from=PLAYBACK_DATE,
            date_to=PLAYBACK_DATE,
            user_id=user.id,
            ad_type="reward_video",
            app_env="prod",
            revenue_scope="all",
            granularity="hour",
        )

        revenue_by_status = {row["status"]: row["revenue_yuan"] for row in result["items"]}
        assert revenue_by_status == {
            "closed_early": 0.0,
            "too_short": 0.0,
            "capped": 0.1,
            "granted": 0.1,
        }
        assert result["total_impressions"] == 4
        assert result["total_revenue_yuan"] == 0.2
        assert len(result["daily"]) == 1
        assert result["daily"][0]["date"] == PLAYBACK_DATE
        assert result["daily"][0]["impressions"] == 4
        assert result["daily"][0]["revenue_yuan"] == 0.2
        assert sum(row["revenue_yuan"] for row in result["hourly"]) == 0.2
        assert result["type_stats"]["reward_video"] == {
            "impressions": 4,
            "revenue_yuan": 0.2,
        }
    finally:
        db.rollback()
        db.execute(delete(AdRewardRecord).where(AdRewardRecord.reward_date == PLAYBACK_DATE))
        db.execute(delete(AdEcpmRecord).where(AdEcpmRecord.report_date == PLAYBACK_DATE))
        db.execute(delete(User).where(User.phone == phone))
        db.commit()
        db.close()


def test_business_category_ecpm_merges_draw_feed_and_both_video_types() -> None:
    db = SessionLocal()
    phone = "18800009993"
    category_date = "2040-02-05"
    try:
        user = User(phone=phone, username="29999999993", register_channel="sms")
        db.add(user)
        db.flush()
        db.add_all([
            AdEcpmRecord(
                user_id=user.id, ad_type="draw", ad_session_id="category-draw",
                app_env="prod", our_code_id="prod-draw", ecpm_raw="10000",
                report_date=category_date, created_at=datetime(2040, 2, 5, 1, tzinfo=UTC),
            ),
            AdEcpmRecord(
                user_id=user.id, ad_type="feed", ad_session_id="category-feed",
                app_env="prod", our_code_id="prod-draw", ecpm_raw="20000",
                report_date=category_date, created_at=datetime(2040, 2, 5, 2, tzinfo=UTC),
            ),
            AdEcpmRecord(
                user_id=user.id, ad_type="reward_video", ad_session_id="category-reward",
                app_env="prod", our_code_id="prod-reward", ecpm_raw="30000",
                report_date=category_date, created_at=datetime(2040, 2, 5, 3, tzinfo=UTC),
            ),
            AdEcpmRecord(
                user_id=user.id, ad_type="withdrawal_video", ad_session_id="category-withdraw",
                app_env="prod", our_code_id="prod-reward", ecpm_raw="50000",
                report_date=category_date, created_at=datetime(2040, 2, 5, 4, tzinfo=UTC),
            ),
        ])
        db.commit()

        result = ad_revenue.ad_revenue_report(
            db,
            date_from=category_date,
            date_to=category_date,
            user_id=user.id,
            app_env="prod",
            revenue_scope="all",
        )

        assert result["category_stats"] == {
            "draw": {"impressions": 2, "revenue_yuan": 0.3, "ecpm_yuan": 150.0},
            "video": {"impressions": 2, "revenue_yuan": 0.8, "ecpm_yuan": 400.0},
        }
    finally:
        db.rollback()
        db.execute(delete(AdEcpmRecord).where(AdEcpmRecord.report_date == category_date))
        db.execute(delete(User).where(User.phone == phone))
        db.commit()
        db.close()


def test_feed_reward_details_keep_each_record_adn() -> None:
    db = SessionLocal()
    phone = "18800009994"
    detail_date = "2040-02-06"
    try:
        user = User(phone=phone, username="29999999994", register_channel="sms")
        db.add(user)
        db.flush()
        db.add_all([
            AdFeedRewardRecord(
                client_event_id="detail-adn-pangle", user_id=user.id,
                reward_date=detail_date, duration_seconds=20, unit_count=1,
                ecpm_raw="12000", adn="pangle", slot_id="rit-pangle",
                ad_type="draw", feed_scene="coupon", trace_id="detail-adn-trace",
                app_env="prod", our_code_id="104098712", coin=12, status="granted",
                created_at=datetime(2040, 2, 6, 1, tzinfo=UTC),
            ),
            AdFeedRewardRecord(
                client_event_id="detail-adn-gdt", user_id=user.id,
                reward_date=detail_date, duration_seconds=20, unit_count=1,
                ecpm_raw="25000", adn="gdt", slot_id="rit-gdt",
                ad_type="draw", feed_scene="coupon", trace_id="detail-adn-trace",
                app_env="prod", our_code_id="104098712", coin=25, status="granted",
                created_at=datetime(2040, 2, 6, 2, tzinfo=UTC),
            ),
        ])
        db.commit()

        result = ad_revenue.ad_revenue_report(
            db, date_from=detail_date, date_to=detail_date,
            user_id=user.id, app_env="prod", revenue_scope="all",
        )

        item = next(row for row in result["items"] if row["event_key"].startswith("feedgrp-"))
        assert [detail["adn"] for detail in item["sub_rewards"]] == ["pangle", "gdt"]
        assert [detail["slot_id"] for detail in item["sub_rewards"]] == ["rit-pangle", "rit-gdt"]
    finally:
        db.rollback()
        db.execute(delete(AdFeedRewardRecord).where(AdFeedRewardRecord.reward_date == detail_date))
        db.execute(delete(User).where(User.phone == phone))
        db.commit()
        db.close()


def test_feed_reward_source_fallback_requires_unique_trace_and_ecpm() -> None:
    db = SessionLocal()
    phone = "18800009995"
    fallback_date = "2040-02-07"
    try:
        user = User(phone=phone, username="29999999995", register_channel="sms")
        db.add(user)
        db.flush()
        db.add_all([
            AdFeedRewardRecord(
                client_event_id="source-fallback-unique", user_id=user.id,
                reward_date=fallback_date, duration_seconds=3, unit_count=0,
                ecpm_raw="4700", ad_session_id="flow-session", trace_id="source-trace",
                app_env="prod", our_code_id="104098712", coin=0, status="too_short",
                ad_type="draw", feed_scene="comparison",
                created_at=datetime(2040, 2, 7, 1, tzinfo=UTC),
            ),
            AdFeedRewardRecord(
                client_event_id="source-fallback-ambiguous", user_id=user.id,
                reward_date=fallback_date, duration_seconds=3, unit_count=0,
                ecpm_raw="4800", ad_session_id="flow-session", trace_id="source-trace",
                app_env="prod", our_code_id="104098712", coin=0, status="too_short",
                ad_type="draw", feed_scene="comparison",
                created_at=datetime(2040, 2, 7, 2, tzinfo=UTC),
            ),
            AdEcpmRecord(
                user_id=user.id, ad_type="draw", ad_session_id="impression-unique",
                trace_id="source-trace", ecpm_raw="4700", adn="baidu", slot_id="rit-baidu",
                app_env="prod", our_code_id="104098712", report_date=fallback_date,
                created_at=datetime(2040, 2, 7, 1, tzinfo=UTC),
            ),
            AdEcpmRecord(
                user_id=user.id, ad_type="draw", ad_session_id="impression-ambiguous-a",
                trace_id="source-trace", ecpm_raw="4800", adn="baidu", slot_id="rit-baidu",
                app_env="prod", our_code_id="104098712", report_date=fallback_date,
                created_at=datetime(2040, 2, 7, 2, tzinfo=UTC),
            ),
            AdEcpmRecord(
                user_id=user.id, ad_type="draw", ad_session_id="impression-ambiguous-b",
                trace_id="source-trace", ecpm_raw="4800", adn="ks", slot_id="rit-ks",
                app_env="prod", our_code_id="104098712", report_date=fallback_date,
                created_at=datetime(2040, 2, 7, 2, 1, tzinfo=UTC),
            ),
        ])
        db.commit()

        result = ad_revenue.ad_revenue_report(
            db, date_from=fallback_date, date_to=fallback_date,
            user_id=user.id, app_env="prod", revenue_scope="all",
        )
        item = next(row for row in result["items"] if row["event_key"].startswith("feedgrp-"))
        details = {detail["ecpm"]: detail for detail in item["sub_rewards"]}
        assert details["4700"]["adn"] == "baidu"
        assert details["4700"]["slot_id"] == "rit-baidu"
        assert details["4800"]["adn"] is None
        assert details["4800"]["slot_id"] is None
    finally:
        db.rollback()
        db.execute(delete(AdFeedRewardRecord).where(AdFeedRewardRecord.reward_date == fallback_date))
        db.execute(delete(AdEcpmRecord).where(AdEcpmRecord.report_date == fallback_date))
        db.execute(delete(User).where(User.phone == phone))
        db.commit()
        db.close()
