"""比价记录 CRUD:上报 upsert(按 user_id+trace_id 幂等) + 派生字段 + 明细分页。

派生逻辑:best_* / saved_amount_cents / is_source_best 全部从 comparison_results 算出
(协议保证已按 price 升序、rank=1 最便宜),客户端不用自己算、也不可信它算。
"""
from __future__ import annotations

from datetime import datetime

from sqlalchemy import func, select
from sqlalchemy.orm import Session

from app.core.rewards import CN_TZ
from app.models.ad_feed_reward import AdFeedRewardRecord
from app.models.comparison import ComparisonRecord
from app.models.savings import SavingsRecord
from app.schemas.compare_record import ComparisonRecordIn


def _yuan_to_cents(yuan: float | None) -> int | None:
    """元(float)→ 分(int)。None 透传。"""
    if yuan is None:
        return None
    return round(yuan * 100)


def _derive(payload: ComparisonRecordIn) -> dict:
    """从上报 payload 派生结构化列(best/saved/is_source_best/status)。"""
    results = payload.comparison_results

    # 最优 = rank 最小的一条;协议已升序,但不信顺序,显式按 rank/price 兜底取最小价。
    best = None
    priced = [r for r in results if r.price is not None]
    if priced:
        best = min(
            priced,
            key=lambda r: (r.rank if r.rank is not None else 10**9, r.price),
        )

    source_price_cents = _yuan_to_cents(payload.source_price)
    if source_price_cents is None:
        # 源价没单独给,从 comparison_results 里的 is_source 行兜底
        src_row = next((r for r in results if r.is_source and r.price is not None), None)
        if src_row is not None:
            source_price_cents = _yuan_to_cents(src_row.price)

    best_price_cents = _yuan_to_cents(best.price) if best else None

    saved_amount_cents = None
    if source_price_cents is not None and best_price_cents is not None:
        saved_amount_cents = source_price_cents - best_price_cents

    is_source_best = best.is_source if best is not None else None

    # status:客户端显式给了就用;否则有"非源且有价"的结果=success,否则 failed
    status = payload.status
    if status is None:
        has_valid_target = any(
            (not r.is_source) and r.price is not None for r in results
        )
        status = "success" if has_valid_target else "failed"

    return {
        "source_price_cents": source_price_cents,
        "best_platform_id": best.platform_id if best else None,
        "best_platform_name": best.platform_name if best else None,
        "best_price_cents": best_price_cents,
        "saved_amount_cents": saved_amount_cents,
        "is_source_best": is_source_best,
        "status": status,
    }


def upsert_record(
    db: Session, *, user_id: int, payload: ComparisonRecordIn
) -> ComparisonRecord:
    """按 (user_id, trace_id) 幂等写入:已存在则覆盖(更完整的重试上报胜出),否则新建。"""
    derived = _derive(payload)
    fields = dict(
        device_id=payload.device_id,
        business_type=payload.business_type,
        store_name=payload.store_name,
        source_platform_id=payload.source_platform_id,
        source_platform_name=payload.source_platform_name,
        source_package=payload.source_package,
        information=payload.information,
        best_deeplink=payload.best_deeplink,
        trace_url=payload.trace_url,
        total_dish_count=payload.total_dish_count,
        skipped_dish_count=payload.skipped_dish_count,
        items=[it.model_dump(exclude_none=True) for it in payload.items],
        comparison_results=[r.model_dump() for r in payload.comparison_results],
        skipped_dish_names=list(payload.skipped_dish_names),
        # 客户端环境 / 性能(debug,客户端上报;旧客户端为 None)
        device_model=payload.device_model,
        device_manufacturer=payload.device_manufacturer,
        rom_vendor=payload.rom_vendor,
        rom_name=payload.rom_name,
        rom_version=payload.rom_version,
        android_version=payload.android_version,
        android_sdk=payload.android_sdk,
        app_version=payload.app_version,
        app_version_code=payload.app_version_code,
        source_app_version=payload.source_app_version,
        longitude=payload.longitude,
        latitude=payload.latitude,
        total_ms=payload.total_ms,
        step_count=payload.step_count,
        raw_payload=payload.model_dump(),
        **derived,
    )

    existing = db.execute(
        select(ComparisonRecord).where(
            ComparisonRecord.user_id == user_id,
            ComparisonRecord.trace_id == payload.trace_id,
        )
    ).scalar_one_or_none()

    if existing is not None:
        for k, v in fields.items():
            setattr(existing, k, v)
        db.commit()
        db.refresh(existing)
        return existing

    # created_at 显式存 naive 北京 wall-clock(同 savings_record):客户端「比价记录」页
    # formatRecordTime 原样切片 created_at 字符串、不转时区,而默认 server_default=func.now()
    # 在 SQLite 下返回 UTC → 直接慢 8h。覆盖分支不动 created_at(保留首次创建时间)。
    rec = ComparisonRecord(
        user_id=user_id,
        trace_id=payload.trace_id,
        created_at=datetime.now(CN_TZ).replace(tzinfo=None),
        **fields,
    )
    db.add(rec)
    db.commit()
    db.refresh(rec)
    return rec


def _ordered_shop_names(db: Session, user_id: int) -> set[str]:
    """该用户「真实下单」(source='compare')覆盖到的店名集合,用来给比价记录打「已下单」。

    只认 compare(归因命中后真实上报),demo 演示数据不算。下单上报不带 trace_id,
    只能按店名对齐——两边店名同源(都来自比价意图识别阶段的门店名 query),精确相等即视为同店。
    语义=店级:同一家店比价过多次,这些记录会一并标「已下单」。
    """
    rows = db.execute(
        select(SavingsRecord.shop_name).where(
            SavingsRecord.user_id == user_id,
            SavingsRecord.source == "compare",
            SavingsRecord.shop_name.is_not(None),
        )
    ).scalars().all()
    return {s for s in rows if s}


def _ad_coins_by_trace(db: Session, user_id: int, trace_ids: list[str]) -> dict[str, int]:
    """各 trace_id「看广告赚的金币」:按 trace_id 聚合该用户已发放(granted)的信息流广告金币。

    广告金币来自比价等候期信息流(biz_type=feed_ad_reward);客户端结算时把本场 trace_id 带上,
    落到 ad_feed_reward_record.trace_id。此处按 trace_id 求和 = 这次比价看广告实发的金币。
    trace_id 仅比价场景客户端带(领券/福利/旧客户端为 NULL),故按 trace_id 过滤天然只算比价广告。
    空集合直接返回(避免 IN () 非法)。
    """
    if not trace_ids:
        return {}
    rows = db.execute(
        select(
            AdFeedRewardRecord.trace_id,
            func.coalesce(func.sum(AdFeedRewardRecord.coin), 0),
        ).where(
            AdFeedRewardRecord.user_id == user_id,
            AdFeedRewardRecord.status == "granted",
            AdFeedRewardRecord.trace_id.in_(trace_ids),
        ).group_by(AdFeedRewardRecord.trace_id)
    ).all()
    return {tid: int(coin) for tid, coin in rows if tid}


def list_records(
    db: Session,
    user_id: int,
    *,
    limit: int = 20,
    cursor: int | None = None,
) -> tuple[list[ComparisonRecord], int | None]:
    """比价记录分页(按创建时间倒序、id 兜底,游标式)。附「已下单」店级标记 + 「看广告赚的金币」(瞬态,不写库)。"""
    stmt = select(ComparisonRecord).where(ComparisonRecord.user_id == user_id)
    if cursor is not None:
        stmt = stmt.where(ComparisonRecord.id < cursor)
    stmt = stmt.order_by(ComparisonRecord.created_at.desc(), ComparisonRecord.id.desc()).limit(limit)

    items = list(db.execute(stmt).scalars().all())
    next_cursor = items[-1].id if len(items) == limit else None

    # 「已下单」标记:本页记录的 store_name 若落在该用户真实下单的店名集合里即 True。
    # ordered / ad_coins_earned 均非 ORM 列,仅挂实例上供 ComparisonRecordOut(from_attributes) 读出,不持久化。
    ordered_shops = _ordered_shop_names(db, user_id)
    # 「本次比价看广告赚的金币」:按本页 trace_id 一次性聚合(同 ordered 范式)。
    ad_coins = _ad_coins_by_trace(db, user_id, [it.trace_id for it in items])
    for it in items:
        it.ordered = bool(it.store_name and it.store_name in ordered_shops)
        it.ad_coins_earned = ad_coins.get(it.trace_id, 0)

    return items, next_cursor


def count_success(db: Session, user_id: int) -> int:
    """该用户成功比价(status='success')的条数。比价战绩里程碑的解锁进度源。"""
    return db.execute(
        select(func.count(ComparisonRecord.id)).where(
            ComparisonRecord.user_id == user_id,
            ComparisonRecord.status == "success",
        )
    ).scalar_one()


def get_stats(db: Session, user_id: int) -> tuple[int, int]:
    """「我的」页省钱战绩卡的**比价口径**聚合:(成功比价数, 累计发现可省额/分)。

    - 完成比价 = status='success' 的比价记录数(同 count_success)。
    - 累计发现可省 = 各成功比价 saved_amount_cents(源价−最低价)之和;null(没省/未识别)不计。
    与「下单成交记账」savings_record 解耦:比过价就计,不要求下单(见 savings.py)。
    """
    row = db.execute(
        select(
            func.count(ComparisonRecord.id),
            func.coalesce(func.sum(ComparisonRecord.saved_amount_cents), 0),
        ).where(
            ComparisonRecord.user_id == user_id,
            ComparisonRecord.status == "success",
        )
    ).one()
    return int(row[0]), int(row[1])


def get_record(db: Session, user_id: int, record_id: int) -> ComparisonRecord | None:
    """取单条(限本人,避免越权读他人记录)。附「看广告赚的金币」瞬态属性(同 list_records)。"""
    rec = db.execute(
        select(ComparisonRecord).where(
            ComparisonRecord.id == record_id,
            ComparisonRecord.user_id == user_id,
        )
    ).scalar_one_or_none()
    if rec is not None:
        rec.ad_coins_earned = _ad_coins_by_trace(db, user_id, [rec.trace_id]).get(rec.trace_id, 0)
    return rec
