"""
OKR目标管理 API — 季度目标 + 关键结果 + KPI联动
KR完整修复(2026-08-27): KR读取从ActionPlan改为krs表 + 方向符号operator + 权重
"""
from fastapi import APIRouter, Depends, HTTPException, Query
from sqlalchemy.orm import Session
from sqlalchemy import func
from datetime import datetime, timedelta, date as date_type
from typing import Optional
from app.database import get_db
from app.deps import get_entity_id
from app.auth_middleware import require_role
from app.models import Objective, ActionPlan, KR, ObjectiveKPI, KPIDefinition

router = APIRouter(prefix="/api/cma/okr", tags=["OKR目标管理"],
    dependencies=[Depends(require_role("ceo", "finance", "it"))],
)


# ── KR 工具函数 ─────────────────────────────────────────

VALID_OPERATORS = (">=", "<=", ">", "<", "=")


def _num(v):
    """Decimal → float 便于 JSON 序列化"""
    return float(v) if v is not None else None


def _parse_operator(raw):
    """解析方向符号: 支持 >=/<=/>/</=，非法默认 >="""
    if not raw:
        return ">="
    op = str(raw).strip()
    if op in VALID_OPERATORS:
        return op
    # 兼容中文符号 ≥ ≤
    if op in ("≥",):
        return ">="
    if op in ("≤",):
        return "<="
    return ">="


def _eval_kr(kr: KR) -> bool:
    """达成判断: 实际值满足 operator → True"""
    if kr.current_value is None or kr.target_value is None:
        return False
    cur = float(kr.current_value)
    tgt = float(kr.target_value)
    op = _parse_operator(kr.operator)
    if op == ">=":
        ok = cur >= tgt
    elif op == "<=":
        ok = cur <= tgt
    elif op == ">":
        ok = cur > tgt
    elif op == "<":
        ok = cur < tgt
    elif op == "=":
        tol = float(kr.tolerance) if kr.tolerance is not None else 0.0
        ok = abs(cur - tgt) <= tol
    else:
        ok = False
    return ok


def _calc_progress(kr: KR) -> int:
    """方向感知进度计算:
      >=/>: current/target*100
      <=/<: target/current*100
      =  : 容差内100
    """
    if kr.current_value is None or kr.target_value is None:
        return kr.progress or 0
    cur = float(kr.current_value)
    tgt = float(kr.target_value)
    if tgt == 0:
        return 100 if cur == 0 else (100 if cur > 0 else 0)
    op = _parse_operator(kr.operator)
    if op in (">=", ">"):
        pct = cur / tgt * 100
    elif op in ("<=", "<"):
        # 值越小进度越高（如费用率<=18%）
        if cur <= tgt:
            pct = 100.0
        else:
            pct = max(0.0, tgt / cur * 100)
    elif op == "=":
        tol = float(kr.tolerance) if kr.tolerance is not None else 0.0
        if abs(cur - tgt) <= tol:
            pct = 100.0
        else:
            pct = max(0.0, 100.0 - abs(cur - tgt) / (abs(tgt) or 1) * 100)
    else:
        pct = cur / tgt * 100
    return max(0, min(100, round(pct)))


def _refresh_kr_state(kr: KR):
    """按当前值刷新 progress + status（方向感知）"""
    if kr.current_value is not None and kr.target_value is not None:
        kr.progress = _calc_progress(kr)
        if _eval_kr(kr):
            kr.status = "achieved"
        elif kr.progress > 0:
            kr.status = "in_progress"
    return kr


def _inherit_kpi_direction(db: Session, kpi_id, operator, target_value):
    """关联KPI时自动继承方向: 从KPI的threshold_green解析(如 '>=800' / '<=18')"""
    if not kpi_id:
        return operator, target_value
    kpi = db.query(KPIDefinition).filter(KPIDefinition.id == kpi_id).first()
    if not kpi:
        return operator, target_value
    threshold = (kpi.threshold_green or "").strip()
    if not threshold:
        return operator, target_value
    op = operator
    tgt = target_value
    for cand in VALID_OPERATORS:
        if threshold.startswith(cand):
            op = cand
            try:
                tgt = float(threshold[len(cand):].strip())
            except (ValueError, TypeError):
                pass
            break
    else:
        # 无符号时尝试解析纯数字
        try:
            tgt = float(threshold)
        except (ValueError, TypeError):
            pass
    return op, tgt


def _kr_serialize(kr: KR, db: Session = None) -> dict:
    """KR → dict（含operator/weight/tolerance）"""
    mkpi = None
    if db and kr.metric_kpi_id:
        mkpi = db.query(KPIDefinition).filter(KPIDefinition.id == kr.metric_kpi_id).first()
    return {
        "id": kr.id,
        "objective_id": kr.objective_id,
        "title": kr.title,
        "metric_kpi_id": kr.metric_kpi_id,
        "metric_kpi_code": mkpi.kpi_code if mkpi else None,
        "metric_kpi_name": mkpi.kpi_name if mkpi else None,
        "operator": _parse_operator(kr.operator),
        "tolerance": _num(kr.tolerance),
        "weight": _num(kr.weight),
        "target_value": _num(kr.target_value),
        "current_value": _num(kr.current_value),
        "progress": kr.progress,
        "status": kr.status,
        "due_date": kr.due_date.isoformat() if kr.due_date else None,
        "monthly_milestones": kr.monthly_milestones or [],
    }


def _get_objective_or_404(db: Session, obj_id: int, entity_id: int) -> Objective:
    obj = db.query(Objective).filter(Objective.id == obj_id, Objective.entity_id == entity_id).first()
    if not obj:
        raise HTTPException(404, "目标不存在")
    return obj


# ── 目标列表/详情 ─────────────────────────────────────

@router.get("")
def list_objectives(
    quarter: Optional[str] = Query(None, description="筛选季度: 2026Q3"),
    dimension: Optional[str] = Query(None),
    status: Optional[str] = Query(None),
    db: Session = Depends(get_db),
    entity_id: int = Depends(get_entity_id),
):
    """列出OKR目标（账套隔离: 按token企业） — KR读取自krs表(2026-08-27)"""
    q = db.query(Objective).filter(Objective.entity_id == entity_id)
    if quarter:
        q = q.filter(Objective.quarter == quarter)
    if dimension:
        q = q.filter(Objective.dimension == dimension)
    if status:
        q = q.filter(Objective.status == status)
    objs = q.order_by(Objective.quarter.desc(), Objective.id).all()
    results = []
    for o in objs:
        krs = db.query(KR).filter(KR.objective_id == o.id).order_by(KR.id).all()
        kr_summary = [
            {"id": kr.id, "title": kr.title, "status": kr.status, "progress": kr.progress,
             "operator": _parse_operator(kr.operator), "weight": _num(kr.weight),
             "metric_kpi_id": kr.metric_kpi_id}
            for kr in krs
        ]
        results.append({
            "id": o.id, "title": o.title, "description": o.description,
            "dimension": o.dimension, "quarter": o.quarter,
            "owner": o.owner, "status": o.status, "progress": o.progress,
            "confidence": o.confidence,
            "key_results": kr_summary,
            "kr_count": len(krs),
            "kr_completed": sum(1 for kr in krs if kr.status in ("completed", "achieved")),
            "created_at": o.created_at.isoformat() if o.created_at else None,
        })
    return {"total": len(results), "items": results}


@router.post("")
def create_objective(
    data: dict,
    db: Session = Depends(get_db),
    entity_id: int = Depends(get_entity_id),
):
    """创建OKR目标（支持JSON Body和Query参数两种方式）— 账套隔离: 强制token企业"""
    title = data.get("title") or ""
    quarter = data.get("quarter") or ""
    description = data.get("description")
    dimension = data.get("dimension")
    owner = data.get("owner")
    if not title or not quarter:
        raise HTTPException(422, "缺少必填字段: title, quarter")
    obj = Objective(title=title, quarter=quarter, description=description,
                    dimension=dimension, owner=owner, entity_id=entity_id)
    db.add(obj)
    db.commit()
    db.refresh(obj)
    return {"ok": True, "id": obj.id, "title": obj.title}


@router.get("/{obj_id}")
def get_objective(obj_id: int, db: Session = Depends(get_db), entity_id: int = Depends(get_entity_id)):
    """获取单个OKR详情（账套隔离: 跨企业404） — KR读取自krs表(2026-08-27)"""
    obj = _get_objective_or_404(db, obj_id, entity_id)
    krs = db.query(KR).filter(KR.objective_id == obj_id).order_by(KR.id).all()
    return {
        "objective": {
            "id": obj.id, "title": obj.title, "description": obj.description,
            "dimension": obj.dimension, "quarter": obj.quarter,
            "owner": obj.owner, "status": obj.status, "progress": obj.progress,
            "confidence": obj.confidence,
        },
        "key_results": [_kr_serialize(kr, db) for kr in krs],
    }


@router.patch("/{obj_id}")
def update_objective(obj_id: int, db: Session = Depends(get_db), entity_id: int = Depends(get_entity_id)):
    """更新OKR进度（通过krs表KR自动计算加权进度）"""
    obj = _get_objective_or_404(db, obj_id, entity_id)
    krs = db.query(KR).filter(KR.objective_id == obj_id).all()
    if krs:
        weights = [float(kr.weight) if kr.weight else 1.0 for kr in krs]
        total_w = sum(weights) or len(krs)
        obj.progress = round(sum(kr.progress * w for kr, w in zip(krs, weights)) / total_w)
    db.commit()
    return {"ok": True, "id": obj_id, "progress": obj.progress}


# ── KR CRUD ───────────────────────────────────────────

@router.post("/{objective_id}/krs")
def create_kr(objective_id: int, data: dict, db: Session = Depends(get_db),
              entity_id: int = Depends(get_entity_id)):
    """新增KR（落krs表）— title/metric_kpi_id/operator/target_value/weight/progress/due_date
    关联KPI时自动继承方向(threshold_green)
    """
    obj = _get_objective_or_404(db, objective_id, entity_id)
    title = data.get("title") or ""
    if not title:
        raise HTTPException(422, "缺少必填字段: title")
    operator, target_value = _inherit_kpi_direction(
        db, data.get("metric_kpi_id"), data.get("operator"), data.get("target_value"))
    if target_value is None and operator != "=":
        # 没有目标值也能建（后续补），但明确提示
        pass
    kr = KR(
        entity_id=entity_id,
        objective_id=objective_id,
        title=title,
        metric_kpi_id=data.get("metric_kpi_id") or None,
        operator=_parse_operator(operator),
        tolerance=data.get("tolerance"),
        weight=data.get("weight"),
        target_value=target_value,
        current_value=data.get("current_value"),
        progress=data.get("progress", 0),
        status=data.get("status", "pending"),
        due_date=_parse_date(data.get("due_date")),
        monthly_milestones=data.get("monthly_milestones") or [],
    )
    _refresh_kr_state(kr)
    db.add(kr)
    db.commit()
    db.refresh(kr)
    update_objective(objective_id, db, entity_id)
    return {"ok": True, "id": kr.id, "kr": _kr_serialize(kr, db)}


@router.put("/{objective_id}/krs/sync")
def sync_krs(objective_id: int, data: dict, db: Session = Depends(get_db),
             entity_id: int = Depends(get_entity_id)):
    """批量同步KR数组（前端保存整个KR列表时用）:
    body: {"krs": [{id?, title, operator, target_value, weight, kpi_code?, ...}]}
    已存在的按id更新；无id且同名则更新；否则新增；不在列表中的删除
    注意: 必须声明在 /krs/{kr_id} 之前，避免 'sync' 被解析为 kr_id
    """
    obj = _get_objective_or_404(db, objective_id, entity_id)
    incoming = data.get("krs", [])
    if not isinstance(incoming, list):
        raise HTTPException(400, "krs 必须是数组")
    existing = {kr.id: kr for kr in db.query(KR).filter(KR.objective_id == objective_id).all()}
    keep_ids = set()
    now_name = {}

    for i, item in enumerate(incoming):
        title = (item.get("title") or item.get("name") or "").strip()
        if not title:
            continue
        kpi_id = item.get("metric_kpi_id") or item.get("kpi_id")
        # kpi_code → kpi_id 转换
        if not kpi_id and item.get("kpi_code"):
            kpi = db.query(KPIDefinition).filter(
                KPIDefinition.kpi_code == item["kpi_code"],
                KPIDefinition.entity_id == entity_id).first()
            kpi_id = kpi.id if kpi else None
        operator, target_value = _inherit_kpi_direction(db, kpi_id, item.get("operator"), item.get("target_value"))

        kr = None
        if item.get("id") and int(item["id"]) in existing:
            kr = existing[int(item["id"])]
        elif title in now_name:
            kr = now_name[title]
        else:
            # 按同名找已有
            for k in existing.values():
                if k.title == title:
                    kr = k
                    break
        if kr is None:
            kr = KR(entity_id=entity_id, objective_id=objective_id, title=title)
            db.add(kr)
        kr.title = title
        kr.metric_kpi_id = kpi_id
        kr.operator = _parse_operator(operator)
        if item.get("tolerance") is not None:
            kr.tolerance = item.get("tolerance")
        if item.get("weight") is not None:
            kr.weight = item.get("weight")
        if item.get("target_value") is not None:
            kr.target_value = item.get("target_value")
        elif target_value is not None:
            kr.target_value = target_value
        if item.get("current_value") is not None:
            kr.current_value = item.get("current_value")
        if item.get("due_date"):
            kr.due_date = _parse_date(item.get("due_date"))
        if item.get("monthly_milestones") is not None:
            kr.monthly_milestones = item.get("monthly_milestones")
        _refresh_kr_state(kr)
        keep_ids.add(kr.id)
        now_name[title] = kr
    # 删除不在列表中的
    for kid, kr in existing.items():
        if kid not in keep_ids:
            db.delete(kr)
    db.commit()
    update_objective(objective_id, db, entity_id)
    krs = db.query(KR).filter(KR.objective_id == objective_id).order_by(KR.id).all()
    return {"ok": True, "objective_id": objective_id, "krs": [_kr_serialize(kr, db) for kr in krs]}


@router.put("/{objective_id}/krs/{kr_id}")
def update_kr(objective_id: int, kr_id: int, data: dict, db: Session = Depends(get_db),
              entity_id: int = Depends(get_entity_id)):
    """更新KR（含方向/权重/当前值，自动重算progress+status）"""
    obj = _get_objective_or_404(db, objective_id, entity_id)
    kr = db.query(KR).filter(KR.id == kr_id, KR.objective_id == objective_id,
                             KR.entity_id == entity_id).first()
    if not kr:
        raise HTTPException(404, "KR不存在或不属于该目标")
    updatable = {"title", "metric_kpi_id", "operator", "tolerance", "weight",
                 "target_value", "current_value", "progress", "status", "due_date",
                 "monthly_milestones"}
    for k, v in data.items():
        if k in updatable and v is not None:
            setattr(kr, k, v)
    # 关联KPI变化 → 自动继承方向
    if "metric_kpi_id" in data or "operator" not in data or "target_value" not in data:
        if data.get("metric_kpi_id") and ("operator" not in data or "target_value" not in data):
            kr.operator, kr.target_value = _inherit_kpi_direction(
                db, data.get("metric_kpi_id", kr.metric_kpi_id),
                data.get("operator", kr.operator), data.get("target_value", kr.target_value))
    if data.get("due_date"):
        kr.due_date = _parse_date(data["due_date"])
    _refresh_kr_state(kr)
    db.commit()
    db.refresh(kr)
    update_objective(objective_id, db, entity_id)
    return {"ok": True, "id": kr.id, "kr": _kr_serialize(kr, db)}


@router.delete("/{objective_id}/krs/{kr_id}")
def delete_kr(objective_id: int, kr_id: int, db: Session = Depends(get_db),
              entity_id: int = Depends(get_entity_id)):
    """删除KR"""
    _get_objective_or_404(db, objective_id, entity_id)
    kr = db.query(KR).filter(KR.id == kr_id, KR.objective_id == objective_id,
                             KR.entity_id == entity_id).first()
    if not kr:
        raise HTTPException(404, "KR不存在或不属于该目标")
    db.delete(kr)
    db.commit()
    update_objective(objective_id, db, entity_id)
    return {"ok": True, "deleted": kr_id}


# ── KR里程碑（时间分解） ─────────────────────────────

@router.put("/{okr_id}/decomposition/milestones/{kr_id}")
def update_kr_milestones(okr_id: int, kr_id: int, data: dict, db: Session = Depends(get_db),
                         entity_id: int = Depends(get_entity_id)):
    """保存KR的月度里程碑（时间分解视图编辑） — 同步更新KR进度"""
    _get_objective_or_404(db, okr_id, entity_id)
    kr = db.query(KR).filter(KR.id == kr_id, KR.objective_id == okr_id,
                             KR.entity_id == entity_id).first()
    if not kr:
        raise HTTPException(404, "KR不存在或不属于该OKR")
    milestones = data.get("milestones")
    if not isinstance(milestones, list):
        raise HTTPException(400, "milestones 必须是数组")
    normalized = []
    for ms in milestones:
        normalized.append({
            "month": ms.get("month", ""),
            "label": ms.get("label", ""),
            "status": ms.get("status", "pending"),
            "target": ms.get("target"),
        })
    kr.monthly_milestones = normalized
    if normalized:
        done = sum(1 for ms in normalized if ms.get("status") == "completed")
        kr.progress = round(done / len(normalized) * 100)
        if kr.progress >= 100:
            kr.status = "achieved"
        elif kr.status == "achieved" and kr.progress < 100:
            kr.status = "in_progress"
    db.commit()
    return {"ok": True, "kr_id": kr.id, "progress": kr.progress, "milestones": kr.monthly_milestones}


@router.post("/{okr_id}/decomposition/milestones/generate")
def auto_generate_milestones(okr_id: int, data: dict, db: Session = Depends(get_db),
                             entity_id: int = Depends(get_entity_id)):
    """为KR自动生成月度里程碑（按季度剩余月份均分）"""
    _get_objective_or_404(db, okr_id, entity_id)
    kr_id = data.get("kr_id")
    if not kr_id:
        raise HTTPException(400, "缺少 kr_id")
    kr = db.query(KR).filter(KR.id == kr_id, KR.objective_id == okr_id,
                             KR.entity_id == entity_id).first()
    if not kr:
        raise HTTPException(404, "KR不存在或不属于该OKR")
    kr.monthly_milestones = _build_auto_milestones(kr)
    db.commit()
    return {"ok": True, "kr_id": kr.id, "milestones": kr.monthly_milestones}


def _build_auto_milestones(kr: KR) -> list:
    """按截止日期向前均分3个月度里程碑"""
    base = kr.due_date or datetime.now() + timedelta(days=90)
    if isinstance(base, str):
        try:
            base = datetime.strptime(base[:10], "%Y-%m-%d").date()
        except ValueError:
            base = datetime.now().date() + timedelta(days=90)
    if isinstance(base, datetime):
        base = base.date()
    base = base.replace(day=1)
    month_keys = []
    for i in range(3, 0, -1):
        y, m = base.year, base.month - i
        while m <= 0:
            m += 12
            y -= 1
        month_keys.append(f"{y:04d}-{m:02d}")
    return [
        {
            "month": mk,
            "label": f"里程碑{i + 1}",
            "status": "pending",
            "target": None,
        }
        for i, mk in enumerate(month_keys)
    ]


def _parse_date(v):
    """兼容 str/date/datetime → date"""
    if v is None or v == "":
        return None
    if isinstance(v, date_type):
        return v
    if isinstance(v, datetime):
        return v.date()
    try:
        return datetime.strptime(str(v)[:10], "%Y-%m-%d").date()
    except (ValueError, TypeError):
        return None


@router.get("/{okr_id}/decomposition")
def get_okr_decomposition(okr_id: int, db: Session = Depends(get_db),
                          entity_id: int = Depends(get_entity_id)):
    """获取OKR的时间分解视图数据 — KR读取自krs表(2026-08-27)"""
    okr = _get_objective_or_404(db, okr_id, entity_id)

    # 1. 关联的BSC战略O（年度 — 相同维度且没有季度标识）
    bsc_o = db.query(Objective).filter(
        Objective.dimension == okr.dimension,
        Objective.quarter.is_(None),
        Objective.entity_id == entity_id,
    ).first()

    # 2. 本OKR的所有KR（krs表）
    krs = db.query(KR).filter(KR.objective_id == okr_id).order_by(KR.id).all()

    # 3. 当前周的ActionPlan（本周行动计划）
    now = datetime.now()
    week_start = now - timedelta(days=now.weekday())
    week_start = week_start.replace(hour=0, minute=0, second=0, microsecond=0)
    week_end = week_start + timedelta(days=7)
    action_plans = db.query(ActionPlan).filter(
        ActionPlan.objective_id == okr_id,
        ActionPlan.due_date.between(week_start, week_end)
    ).all()

    return {
        "annual_o": bsc_o.title if bsc_o else None,
        "quarterly_o": okr.title,
        "krs": [
            {
                "kr_id": kr.id,
                "title": kr.title,
                "progress": kr.progress,
                "milestones": kr.monthly_milestones or []
            }
            for kr in krs
        ],
        "weekly_actions": [
            {
                "id": ap.id,
                "title": ap.title,
                "status": ap.status,
                "deadline": ap.due_date.isoformat() if ap.due_date else None
            }
            for ap in action_plans
        ]
    }
