"""
app/api/v1/tasks.py — 任务路由（查询层）
发起任务 / 任务列表 / 任务详情
操作类端点（选文案/选图/导入/重生成/提审/偏好上下文）在 task_actions.py。
"""

import logging
from typing import Annotated

from fastapi import APIRouter, Depends, Query
from pydantic import BaseModel, field_validator
from sqlalchemy.orm import Session

from app.core.database import get_db
from app.core.response import ok, paginate, raise_not_found
from app.middleware.workspace_guard import CurrentUser, require_write_permission
from app.models.task import GenerationTask, ImageCandidate, TextCandidate

logger = logging.getLogger(__name__)
router = APIRouter(prefix="/tasks", tags=["tasks"])


# ── DTO ────────────────────────────────────────────────────
class CreateTaskRequest(BaseModel):
    product_id: int
    benchmark_ids: list[int] = []
    theme: str | None = None
    text_count: int = 5       # 不写死（基石A），用户设定
    image_count: int = 3      # 不写死（基石A），用户设定
    track: str = "ai"         # ai | import
    need_product_image: bool = True  # 本次产品是否入镜：True=无图禁生成不降级

    @field_validator("text_count", "image_count")
    @classmethod
    def positive(cls, v: int) -> int:
        if v < 1 or v > 20:
            raise ValueError("数量范围 1-20")
        return v

    @field_validator("track")
    @classmethod
    def valid_track(cls, v: str) -> str:
        if v not in ("ai", "import"):
            raise ValueError("track 必须是 ai 或 import")
        return v


class SelectCandidateRequest(BaseModel):
    candidate_id: int


class ImportTextRequest(BaseModel):
    content: str
    angle_label: str | None = None


# ── 格式化辅助 ──────────────────────────────────────────────
def _fmt_task(t: GenerationTask) -> dict:
    return {
        "id": t.id, "product_id": t.product_id, "theme": t.theme,
        "status": t.status, "text_count": t.text_count, "image_count": t.image_count,
        "track": t.track, "need_product_image": t.need_product_image,
        "created_at": t.created_at.isoformat(),
    }


def _score_array_to_obj(arr) -> dict:
    """score_json 是评分明细数组 [{item,score,max,note}]。
    AI评委7维(2026-06-15新版：痛点18/情绪18/买点18/钩子15/标题13/真实感13+合规5)
    或旧机械5维均可处理。
    total 直接对明细求和(不依赖固定key)，透传 dims 给前端 ScoreDimBars 数据驱动渲染。
    同时填旧5维key保持兼容(能映射的填，映射不上的为0)。"""
    # 旧5维 + 新7维维度名 → 旧key 的映射表（兼容新旧两套维度名）
    legacy = {
        # 旧机械维度
        "标题吸引力": "title",   "标题点击力": "title",
        "情绪共鸣":   "emotion", "情绪张力":   "emotion",
        "买点表达":   "selling", "买点转化":   "selling",
        "关键词覆盖": "keyword", "合规性":     "compliance",
        # 新AI评委维度（2026-06-15）→ 最近似旧key
        "痛点人群精准": "keyword",  # 痛点人群精准语义最接近旧关键词(均为"内容精准度")
        "开头钩子":    "emotion",  # 钩子即情绪抓点，归入 emotion
        "真实感":      "selling",  # 真实感/买点转化同属"用户感知"维度
        # "产品聚焦一件事" 已被"真实感"替换，保留映射避免旧存量数据报 KeyError
        "产品聚焦一件事": "selling",
    }
    obj = {"title": 0, "emotion": 0, "selling": 0, "keyword": 0, "compliance": 0,
           "total": 0, "dims": []}
    if isinstance(arr, list):
        total = 0
        for d in arr:
            if not isinstance(d, dict):
                continue
            item = d.get("item", "")
            sc = d.get("score", 0) or 0
            total += sc
            obj["dims"].append({"item": item, "score": sc,
                                "max": d.get("max", 0), "note": d.get("note", "")})
            key = legacy.get(item)
            if key:
                obj[key] = sc
        obj["total"] = max(0, min(100, total))
    return obj


def _fmt_text(tc: TextCandidate) -> dict:
    import json
    # content 列存整条 JSON dict（含 title/content/tags/angle 等），解包后分字段返回
    parsed: dict = {}
    if tc.content:
        try:
            parsed = json.loads(tc.content)
        except (json.JSONDecodeError, ValueError):
            # 兼容旧数据：如果 content 已经是纯正文则原样返回
            parsed = {"content": tc.content}
    score_raw = json.loads(tc.score_json) if tc.score_json else None
    return {
        "candidate_id": tc.id,
        "angle_label": tc.angle_label,
        "title": parsed.get("title", ""),
        "content": parsed.get("content", ""),        # 纯正文，前端直接展示
        "tags": parsed.get("tags", []),
        "cover_title": parsed.get("coverTitle", ""),
        "image_brief": parsed.get("imageBrief", ""),
        "source": tc.source,
        "score": _score_array_to_obj(score_raw),     # 转成 {title,emotion,...,total,dims} 对象
        "banned_word_status": tc.banned_word_status,
        "is_selected": tc.is_selected,
        # AI 评委总评（verdict/summary 存在 content 列，新数据有，旧数据为空字符串降级）
        "verdict": parsed.get("verdict", ""),         # "优秀"|"合格"|"不合格"
        "summary": parsed.get("summary", ""),         # 一句话总评含改进点
    }


def _fmt_image(ic: ImageCandidate) -> dict:
    return {
        "candidate_id": ic.id, "role": ic.role, "url": ic.url,
        "strategy": ic.strategy, "seq": ic.seq, "is_selected": ic.is_selected,
    }


def _check_task_ownership(task: GenerationTask | None, workspace_id: int) -> GenerationTask:
    if not task or task.workspace_id != workspace_id:
        raise_not_found("任务不存在")
    return task


# ── 路由 ───────────────────────────────────────────────────
@router.post("")
def create_task(
    body: CreateTaskRequest,
    current_user: Annotated[CurrentUser, Depends(require_write_permission)],
    db: Session = Depends(get_db),
):
    """发起任务：校验有无 key → 只推 task_id 入队，绝不传 key。"""
    from app.services.task_service import create_generation_task
    task = create_generation_task(db, current_user, body)
    return ok(_fmt_task(task))


@router.get("")
def list_tasks(
    page: int = 1, page_size: int = 20,
    status: list[str] | None = Query(default=None),
    current_user: Annotated[CurrentUser, Depends(require_write_permission)] = None,
    db: Session = Depends(get_db),
):
    q = db.query(GenerationTask).filter(GenerationTask.workspace_id == current_user.workspace_id)
    if status:
        # 支持多状态(?status=approved&status=rejected)，单值也兼容
        q = q.filter(GenerationTask.status.in_(status))
    total = q.count()
    items = q.order_by(GenerationTask.created_at.desc()).offset((page - 1) * page_size).limit(page_size).all()
    return ok(paginate([_fmt_task(t) for t in items], total, page, page_size))


@router.get("/{task_id}")
def get_task(
    task_id: int,
    current_user: Annotated[CurrentUser, Depends(require_write_permission)] = None,
    db: Session = Depends(get_db),
):
    task = db.query(GenerationTask).filter(GenerationTask.id == task_id).first()
    task = _check_task_ownership(task, current_user.workspace_id)
    texts = db.query(TextCandidate).filter(TextCandidate.task_id == task_id).all()
    images = db.query(ImageCandidate).filter(ImageCandidate.task_id == task_id).all()
    return ok({
        **_fmt_task(task),
        "text_candidates": [_fmt_text(tc) for tc in texts],
        "image_candidates": [_fmt_image(ic) for ic in images],
    })
