"""
app/services/flywheel_service.py — 飞轮信号写入 + 偏好上下文聚合
preference_collector：三信号入口（选文案/选图/审核）写入 preference_events。
preference_aggregator：查最近50条 → 最常选角度 + 打回原因近3条原文拼 prompt。
飞轮不暴露独立埋点端点，只由业务接口内部调用（契约红线）。
"""

import logging
from typing import Any

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

from app.constants.enums import SIGNAL_WEIGHTS, DataOwnership, SignalType
from app.middleware.workspace_guard import CurrentUser
from app.models.flywheel import PreferenceEvent
from app.models.task import GenerationTask

logger = logging.getLogger(__name__)

# 实时聚合窗口：最近50条事件
_AGGREGATION_WINDOW = 50
# 冷启动阈值：不足5条信号用产品档案冷启动
_COLD_START_THRESHOLD = 5


def record_signal(
    db: Session,
    current_user: CurrentUser,
    task: GenerationTask,
    signal_type: str,
    candidate_id: int | None = None,
    angle_label: str | None = None,
    reason: str | None = None,
) -> None:
    """
    写入飞轮信号。
    workspace_id + product_id 都必须有（基石C + 按产品分开学）。
    signal_weight 用枚举默认值，北哥可校准。
    data_ownership 默认 client_data（选择行为归客户）。
    """
    weight = SIGNAL_WEIGHTS.get(signal_type, 0)
    event = PreferenceEvent(
        workspace_id=current_user.workspace_id,
        product_id=task.product_id,
        task_id=task.id,
        user_id=current_user.user_id,
        signal_type=signal_type,
        signal_weight=weight,
        candidate_id=candidate_id,
        angle_label=angle_label,
        reason=reason,
        data_ownership=DataOwnership.CLIENT_DATA,
    )
    try:
        db.add(event)
        db.commit()
        logger.info(
            "Flywheel signal: type=%s weight=%s user=%s product=%s",
            signal_type, weight, current_user.user_id, task.product_id,
        )
    except Exception:
        db.rollback()
        logger.error(
            "Failed to write preference_event: type=%s user=%s",
            signal_type, current_user.user_id,
        )
        raise


def get_preference_context(
    db: Session, workspace_id: int, product_id: int
) -> dict[str, Any]:
    """
    实时聚合偏好上下文（最近50条 events）。
    返回：recent_preference摘要 + reject_reasons近3条 + injected_count。
    不足5条 → 冷启动提示（产品档案兜底，由 AIE prompt 层读 products.custom_prompt）。
    按 workspace_id + product_id 严格过滤（不串数据，基石C）。
    """
    recent = (
        db.query(PreferenceEvent)
        .filter(
            PreferenceEvent.workspace_id == workspace_id,
            PreferenceEvent.product_id == product_id,
        )
        .order_by(desc(PreferenceEvent.created_at))
        .limit(_AGGREGATION_WINDOW)
        .all()
    )

    if len(recent) < _COLD_START_THRESHOLD:
        return {
            "recent_preference": "信号不足，使用产品档案基线（冷启动）",
            "reject_reasons": [],
            "injected_count": len(recent),
        }

    # 统计最常被选中的角度
    angle_counts: dict[str, int] = {}
    for ev in recent:
        if ev.signal_type in (SignalType.TEXT_SELECT, SignalType.APPROVE) and ev.angle_label:
            angle_counts[ev.angle_label] = angle_counts.get(ev.angle_label, 0) + 1

    top_angles = sorted(angle_counts.items(), key=lambda x: x[1], reverse=True)[:3]
    if top_angles:
        pref_desc = "；".join(f"{a}（已选{c}次）" for a, c in top_angles)
        preference_summary = f"最近偏好：{pref_desc}"
    else:
        preference_summary = "暂无明显角度偏好"

    # 取最近3条打回原因原文（不做 AI 归纳，契约§3）
    reject_reasons = [
        ev.reason for ev in recent
        if ev.signal_type == SignalType.REJECT_WITH_REASON and ev.reason
    ][:3]

    return {
        "recent_preference": preference_summary,
        "reject_reasons": reject_reasons,
        "injected_count": len(recent),
    }
