"""
app/api/v1/products.py — 品牌库路由（管理员）
products / benchmark_notes / banned_words
category 是纯数据字段，不在代码里做枚举（基石A）。
"""

import logging
from typing import Annotated

from fastapi import APIRouter, Depends, UploadFile, File
from pydantic import BaseModel
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_admin, require_write_permission
from app.models.product import BannedWord, BenchmarkNote, Product

logger = logging.getLogger(__name__)
router = APIRouter(tags=["products"])


# ── DTO ────────────────────────────────────────────────────
class ProductCreate(BaseModel):
    name: str
    category: str | None = None        # 纯数据字段，不做枚举（基石A）
    source: str = "custom"             # preset | custom
    selling_points: list[str] = []
    style_tone: str | None = None
    text_angles: list[str] = []        # 用户设定，不写死（基石A）
    custom_prompt: str | None = None   # 等北哥方案注入
    banned_word_ids: list[int] = []
    image_path: str | None = None      # 产品参考图(可建档即带；通常走 upload-image 接口)
    brand_keyword: str | None = None   # 品牌词，客户录入，012:套2字段暴露
    target_audience: str | None = None # 目标人群，客户录入，012:套2字段暴露


class BenchmarkCreate(BaseModel):
    screenshot_url: str | None = None
    highlights: str | None = None
    link_url: str | None = None


class BannedWordCreate(BaseModel):
    word: str
    level: str   # auto_fix | soft_warn | hard_block
    replacement: str | None = None
    updatable: bool = True


def _fmt_product(p: Product) -> dict:
    import json
    return {
        "id": p.id, "name": p.name, "category": p.category,
        "source": p.source, "is_active": p.is_active,
        "selling_points": json.loads(p.selling_points) if p.selling_points else [],
        "style_tone": p.style_tone,
        "text_angles": json.loads(p.text_angles) if p.text_angles else [],
        "custom_prompt": p.custom_prompt,
        "image_path": p.image_path,
        "brand_keyword": p.brand_keyword,       # 012: 套2字段暴露
        "target_audience": p.target_audience,   # 012: 套2字段暴露
        "created_at": p.created_at.isoformat(),
    }


# ── 产品档案 ────────────────────────────────────────────────
@router.get("/products")
def list_products(
    page: int = 1, page_size: int = 20, source: str | None = None,
    current_user: Annotated[CurrentUser, Depends(require_write_permission)] = None,
    db: Session = Depends(get_db),
):
    q = db.query(Product).filter(Product.workspace_id == current_user.workspace_id, Product.is_active == True)
    if source in ("preset", "custom"):
        q = q.filter(Product.source == source)
    total = q.count()
    items = q.offset((page - 1) * page_size).limit(page_size).all()
    return ok(paginate([_fmt_product(p) for p in items], total, page, page_size))


@router.post("/products")
def create_product(
    body: ProductCreate,
    current_user: Annotated[CurrentUser, Depends(require_admin)] = None,
    db: Session = Depends(get_db),
):
    import json
    p = Product(
        workspace_id=current_user.workspace_id,
        name=body.name, category=body.category, source=body.source,
        selling_points=json.dumps(body.selling_points, ensure_ascii=False),
        style_tone=body.style_tone,
        text_angles=json.dumps(body.text_angles, ensure_ascii=False),
        custom_prompt=body.custom_prompt,
        image_path=body.image_path or None,
        brand_keyword=body.brand_keyword or None,       # 012: 套2字段
        target_audience=body.target_audience or None,   # 012: 套2字段
    )
    db.add(p)
    db.commit()
    db.refresh(p)
    return ok(_fmt_product(p))


@router.get("/products/{product_id}")
def get_product(
    product_id: int,
    current_user: Annotated[CurrentUser, Depends(require_write_permission)] = None,
    db: Session = Depends(get_db),
):
    p = db.query(Product).filter(Product.id == product_id, Product.workspace_id == current_user.workspace_id).first()
    if not p:
        raise_not_found("产品不存在")
    return ok(_fmt_product(p))


@router.put("/products/{product_id}")
def update_product(
    product_id: int, body: ProductCreate,
    current_user: Annotated[CurrentUser, Depends(require_admin)] = None,
    db: Session = Depends(get_db),
):
    import json
    p = db.query(Product).filter(Product.id == product_id, Product.workspace_id == current_user.workspace_id).first()
    if not p:
        raise_not_found("产品不存在")
    p.name = body.name; p.category = body.category; p.source = body.source
    p.selling_points = json.dumps(body.selling_points, ensure_ascii=False)
    p.style_tone = body.style_tone
    p.text_angles = json.dumps(body.text_angles, ensure_ascii=False)
    p.custom_prompt = body.custom_prompt
    p.brand_keyword = body.brand_keyword or None        # 012: 套2字段
    p.target_audience = body.target_audience or None    # 012: 套2字段
    db.commit(); db.refresh(p)
    return ok(_fmt_product(p))


@router.delete("/products/{product_id}")
def delete_product(
    product_id: int,
    current_user: Annotated[CurrentUser, Depends(require_admin)] = None,
    db: Session = Depends(get_db),
):
    p = db.query(Product).filter(Product.id == product_id, Product.workspace_id == current_user.workspace_id).first()
    if not p:
        raise_not_found("产品不存在")
    p.is_active = False   # 软删
    db.commit()
    return ok({"deleted": product_id})


# ── 产品参考图上传 ──────────────────────────────────────────
_ALLOWED_CONTENT_TYPES = {"image/jpeg", "image/png", "image/webp"}
_MAX_SIZE_BYTES = 10 * 1024 * 1024   # 10 MB


@router.post("/products/{product_id}/upload-image")
async def upload_product_image(
    product_id: int,
    file: UploadFile = File(...),
    current_user: Annotated[CurrentUser, Depends(require_write_permission)] = None,
    db: Session = Depends(get_db),
):
    """
    上传产品参考图（铁律：生图必须带产品图）。
    文件类型：JPEG/PNG/WebP；大小上限 10 MB。
    存储路径：uploads/products/{workspace_id}/{product_id}/{filename}
    写回 product.image_path，生图管道读此字段构建 reference_images。
    """
    from app.core.response import raise_business
    from app.core.config import get_settings
    import os, uuid

    p = db.query(Product).filter(
        Product.id == product_id,
        Product.workspace_id == current_user.workspace_id,
    ).first()
    if not p:
        raise_not_found("产品不存在")

    if file.content_type not in _ALLOWED_CONTENT_TYPES:
        raise_business(f"不支持的文件类型 {file.content_type}，仅支持 JPEG/PNG/WebP")

    data = await file.read()
    if len(data) > _MAX_SIZE_BYTES:
        raise_business("文件超过 10 MB 限制")

    settings = get_settings()
    ext = os.path.splitext(file.filename or "img.jpg")[1] or ".jpg"
    # 存绝对路径：锚定 StaticFiles 根 /app/uploads，避免 worker(cwd=/) 读不到。
    abs_dir = os.path.join(settings.UPLOAD_ABS_ROOT, "products",
                           str(current_user.workspace_id), str(product_id))
    os.makedirs(abs_dir, exist_ok=True)
    filename = f"{uuid.uuid4().hex}{ext}"
    save_path = os.path.join(abs_dir, filename)
    with open(save_path, "wb") as f:
        f.write(data)

    p.image_path = save_path  # 绝对路径，worker 直接 open 可读
    db.commit()
    db.refresh(p)
    logger.info("product image uploaded: product_id=%s path=%s", product_id, save_path)
    return ok(_fmt_product(p))


# ── 套1 产品图视觉分析 ────────────────────────────────────────
_VISION_PROMPT = """你是小红书产品种草策划。请根据产品图分析卖点与人群。
硬性规则：只分析本次上传图片，不沿用历史案例，不默认护肤品。
返回JSON（仅JSON，无其他文字）：
{
  "productName": "从包装识别，识别不到填空",
  "category": "美妆护肤/个护护理/食品饮品/营养健康/家居生活/服饰穿搭/电商产品之一",
  "sellingPoints": ["转成用户买点，不要品牌空话，3-6个"],
  "targetAudience": "一句话描述核心人群",
  "scenarios": ["使用场景，2-4个"],
  "keywords": ["小红书搜索关键词，3-5个"],
  "bannedWords": ["合规禁用词"],
  "imageDirection": "这组图如何拍/排版，一句话",
  "confidence": 0.9,
  "source": "vision"
}"""


def _parse_vision_json(raw: str) -> dict:
    """从模型返回文本中提取JSON（容错 markdown ```json 代码块）"""
    import json, re
    # 去掉 markdown 代码块包裹
    cleaned = re.sub(r"```json\s*", "", raw)
    cleaned = re.sub(r"```\s*", "", cleaned)
    # 提取第一个 {...} 块
    m = re.search(r"\{[\s\S]*\}", cleaned)
    if not m:
        raise ValueError("vision 返回内容中未找到 JSON")
    return json.loads(m.group())


def _fallback_analysis(product_name: str = "") -> dict:
    """vision 失败时的文字兜底，保证接口不空手返回"""
    return {
        "productName": product_name or "产品",
        "category": "电商产品",
        "sellingPoints": ["使用方便", "适合日常场景"],
        "targetAudience": "有明确使用场景和效率诉求的人群",
        "scenarios": ["日常使用", "通勤出门"],
        "keywords": [product_name or "好物", "真实测评", "种草分享"],
        "bannedWords": ["美白", "祛斑", "速效", "医用", "药妆"],
        "imageDirection": "产品白底图保证准确，真实场景图突出使用体验",
        "confidence": 0.45,
        "source": "fallback",
    }


@router.post("/products/{product_id}/analyze-image")
async def analyze_product_image(
    product_id: int,
    file: UploadFile = File(...),
    current_user: Annotated[CurrentUser, Depends(require_write_permission)] = None,
    db: Session = Depends(get_db),
):
    """
    套1：上传产品图 → GPT vision 读图 → 返回结构化卖点/人群分析。
    key 链路：从当前用户 API key 解密（基石B）；plain_key 只活在局部变量。
    """
    from app.core.response import raise_business
    from app.models.workspace import UserApiKey
    from app.utils.fernet_utils import decrypt_key
    from app.services.ai_engine.gemini_factory import build_ai_clients

    # 文件校验（复用 upload_product_image 同款规则）
    if file.content_type not in _ALLOWED_CONTENT_TYPES:
        raise_business(f"不支持的文件类型 {file.content_type}，仅支持 JPEG/PNG/WebP")
    data = await file.read()
    if len(data) > _MAX_SIZE_BYTES:
        raise_business("文件超过 10 MB 限制")

    # key 解密（照抄 pipeline_steps.decrypt_user_key，基石B）
    api_key_row = db.query(UserApiKey).filter(
        UserApiKey.user_id == current_user.user_id,
        UserApiKey.workspace_id == current_user.workspace_id,
        UserApiKey.provider.in_(["openai", "apiports"]),
    ).first()
    if not api_key_row:
        raise_business("未配置 API Key，请先在设置中录入")
    plain_key = decrypt_key(api_key_row.encrypted_key)

    clients = build_ai_clients(plain_key)
    plain_key = None   # 立即清零，不传出（基石B）

    try:
        raw = await clients.gpt_vision_analyze(_VISION_PROMPT, [data])
        try:
            result = _parse_vision_json(raw)
            result["source"] = "vision"
        except Exception as parse_err:
            logger.warning("vision JSON parse failed, fallback: %s", parse_err)
            result = _fallback_analysis()
            result["warning"] = f"视觉分析解析失败，已使用文字兜底：{parse_err}"
    except Exception as e:
        logger.warning("vision_analyze failed, fallback: %s", e)
        result = _fallback_analysis()
        result["warning"] = f"视觉分析失败，已使用文字兜底：{e}"
    finally:
        await clients.aclose()

    logger.info("analyze_product_image done: product_id=%s source=%s conf=%.2f",
                product_id, result.get("source"), result.get("confidence", 0))
    return ok(result)
