"""
自动验证引擎 API — 管理会计OS
POST /api/cma/verify/{plan_id} 手动验证行动计划执行结果

数据流:
  ActionPlan.auto_verify_rule (JSON) → 条件判断 → passed/failed
  → KPI值回填 (kpi_current_before/after + KPIValue source_type=verify)
  → 验证通过 → 所属OKR progress +15%
  → 通知任总 (send_wecom_message)

⚠️ 双 verify 入口关系（2026-08-30 评审收敛，暂不重构）:
  - 本文件: /api/cma/verify/{plan_id} — 完整链路（回填KPIValue + OKR联动 + 企微通知）
  - bot_bridge_v2.py: /api/cma/bot-bridge/verify/{action_plan_id} — 轻量版（仅记 verify_log，
    不回填KPIValue、不联动OKR、不通知），供财务Bot/研学Bot桥接通道调用
  - 两者行为不一致，勿混用：Bot 通道走 bot_bridge_v2，业务侧手动/自动重验走本文件。

规则格式（新）:
  {
    "kpi_code": "C_REBATE_RATE",
    "condition": "LESS_THAN",        # LESS_THAN/GREATER_THAN/WITHIN_RANGE/NOT_NULL
    "target_value": 80,
    "baseline_value": 86.4,
    "verify_after_days": 7,
    "retry_max": 3,
    "escalate_to": "任富海",
    "notify": true
  }

兼容格式（旧）:
  {"condition": "value < 75", "description": "..."}  → 走 bot_bridge_v2._evaluate_condition
"""
from fastapi import APIRouter, Depends, HTTPException
from sqlalchemy.orm import Session
from datetime import datetime, timedelta
import json
import logging

from app.database import get_db
from app.models import ActionPlan, KPIDefinition, KPIValue, Objective
from app.utils.notifier import send_wecom_message
from app.auth_middleware import require_role

logger = logging.getLogger("cma.verify")

router = APIRouter(
    prefix="/api/cma/verify",
    tags=["自动验证"],
    dependencies=[Depends(require_role("ceo", "finance", "business", "it"))],
)

CONDITION_FUNCS = {
    "LESS_THAN": lambda actual, target: actual is not None and actual < target,
    "GREATER_THAN": lambda actual, target: actual is not None and actual > target,
    "WITHIN_RANGE": lambda actual, target: target is not None and len(target) == 2 and float(target[0]) <= actual <= float(target[1]),
    "NOT_NULL": lambda actual, target: actual is not None,
}


def _parse_rule(rule) -> dict:
    """解析auto_verify_rule（兼容JSON字符串）"""
    if rule is None:
        return {}
    if isinstance(rule, str):
        try:
            return json.loads(rule)
        except Exception:
            return {"condition": rule}
    return rule


def evaluate_rule(rule: dict, actual, kpi_data: dict = None) -> bool:
    """
    按规则判断actual是否达标
    - 新格式: condition为枚举 LESS_THAN/GREATER_THAN/WITHIN_RANGE/NOT_NULL
    - 旧格式: condition为表达式字符串（如 "value < 75"）→ bot_bridge_v2引擎
    """
    condition = (rule or {}).get("condition", "")
    if not condition:
        return actual is not None

    if condition in CONDITION_FUNCS:
        target = rule.get("target_value")
        return CONDITION_FUNCS[condition](actual, target)

    # 旧格式表达式（bot-bridge兼容）
    try:
        from app.api.bot_bridge_v2 import _evaluate_condition
        kd = kpi_data or {"value": actual, "target": rule.get("target_value"), "baseline": rule.get("baseline_value")}
        return _evaluate_condition(condition, kd)
    except Exception as e:
        logger.warning(f"旧格式条件评估失败({condition}): {e}")
        return False


def update_okr_progress(db: Session, plan: ActionPlan, already_verified: bool = False, force_recalc: bool = False) -> dict:
    """验证通过 → 所属OKR progress +15%（每通过1个KR）

    缺陷1修复（2026-08-30）：幂等防重复累加
      - already_verified=True（调用前 plan 已 verify_status=='passed' 且 verified_at 非空）
        → 跳过累加，返回当前值（保持原值）
      - force_recalc=True 时强制重新累加（业务确需重验场景由调用方显式开启；默认 False）
    """
    if not plan.objective_id:
        return {"updated": False, "reason": "no_objective"}
    obj = db.query(Objective).filter(Objective.id == plan.objective_id).first()
    if not obj:
        return {"updated": False, "reason": "objective_not_found"}
    before = obj.progress or 0
    if already_verified and not force_recalc:
        return {"updated": False, "reason": "already_verified", "objective_id": obj.id, "before": before, "after": before}
    obj.progress = min(100, before + 15)
    db.flush()
    return {"updated": True, "objective_id": obj.id, "before": before, "after": obj.progress}


def build_auto_verify_rule(kpi, baseline_value=None, verify_after_days: int = 7) -> dict:
    """根据KPI阈值自动生成验证规则（验收#1: 创建ActionPlan自动带auto_verify_rule）

    从KPI的绿灯阈值(green)推导达标方向:
      - 绿灯 '<=X' → 目标是把值压到 X 以下 → LESS_THAN X
      - 绿灯 '>=X' → 目标是把值抬到 X 以上 → GREATER_THAN X
      - 有绿色区间 'X~Y' → WITHIN_RANGE
      - 无阈值 → NOT_NULL
    """
    expr = (kpi.threshold_green or "").strip()
    condition = "NOT_NULL"
    target = None
    import re
    m = re.match(r"^(<=|>=|<|>|=)\s*([\d.]+)$", expr)
    if m:
        op, val = m.group(1), float(m.group(2))
        if op in ("<", "<="):
            condition = "LESS_THAN"
        elif op in (">", ">="):
            condition = "GREATER_THAN"
        else:
            condition = "NOT_NULL"
        target = val
    elif "~" in expr:
        parts = expr.split("~")
        if len(parts) == 2:
            try:
                condition = "WITHIN_RANGE"
                target = [float(parts[0]), float(parts[1])]
            except ValueError:
                pass
    else:
        # 无阈值表达式 → 用KPI目标值推方向
        condition = "NOT_NULL"
        target = None

    return {
        "kpi_code": kpi.kpi_code,
        "condition": condition,
        "target_value": target,
        "baseline_value": float(baseline_value) if baseline_value is not None else None,
        "verify_after_days": verify_after_days,
        "retry_max": 3,
        "escalate_to": "任富海",
        "notify": True,
    }


def backfill_kpi_value(db: Session, plan: ActionPlan, actual, rule: dict, source: str = "verify"):
    """回填KPI当前值: 写入kpi_current_before/after + 新KPIValue记录

    缺陷2修复（2026-08-30）多租户隔离：
      - KPIValue 创建时设置 entity_id（从 plan 关联 KPI 定义取，即 plan.kpi_id → kpi_definitions.entity_id）
      - kpi_code 查询 KPIDefinition 时带 entity_id 过滤（防跨租户误匹配 kpi_code）
    """
    kpi = None
    kpi_code = rule.get("kpi_code") if rule else None
    # 确定 plan 所属 entity_id（从 plan.kpi_id → KPIDefinition.entity_id 向上取）
    entity_id = None
    if plan.kpi_id:
        pkpi = db.query(KPIDefinition).filter(KPIDefinition.id == plan.kpi_id).first()
        if pkpi:
            entity_id = pkpi.entity_id
    # 优先按验证规则指定的KPI编码查询（带 entity_id 过滤）；无规则时才回退到plan.kpi_id
    if kpi_code:
        q = db.query(KPIDefinition).filter(KPIDefinition.kpi_code == kpi_code)
        if entity_id is not None:
            q = q.filter(KPIDefinition.entity_id == entity_id)
        kpi = q.first()
    elif plan.kpi_id:
        kpi = db.query(KPIDefinition).filter(KPIDefinition.id == plan.kpi_id).first()

    if actual is None:
        return None

    plan.kpi_current_before = plan.kpi_current_before if plan.kpi_current_before is not None else rule.get("baseline_value")
    plan.kpi_current_after = actual

    if kpi:
        new_val = KPIValue(
            kpi_id=kpi.id,
            entity_id=kpi.entity_id,  # 缺陷2修复：多租户回填 entity_id
            period=datetime.now().strftime("%Y-%m"),
            actual_value=actual,
            source_type="verify",
            source_batch=f"verify-plan-{plan.id}",
            data_status="verified",
            calculated_at=datetime.now(),
            remark=f"行动计划#{plan.id}验证回填",
        )
        db.add(new_val)
        return {"kpi_id": kpi.id, "kpi_code": kpi.kpi_code}
    return None


@router.post("/{plan_id}")
def verify_action_plan(plan_id: int, payload: dict, db: Session = Depends(get_db)):
    """验证行动计划执行结果（手动验证 / Bot回填）

    请求体:
      {
        "actual_value": 78.5,          # 可选，缺省时取KPI最新值
        "source": "财务Bot分析",        # 来源
        "note": "渠补谈判后...",        # 备注/验证结果详情
        "passed": true                  # 可选，无规则时手动指定
      }
    """
    plan = db.query(ActionPlan).filter(ActionPlan.id == plan_id).first()
    if not plan:
        raise HTTPException(404, "行动计划不存在")

    rule = _parse_rule(plan.auto_verify_rule)
    actual = payload.get("actual_value")
    note = payload.get("note", "")
    source = payload.get("source", "手动验证")

    # 1. 无规则 → 按手动指定 passed 标记
    if not rule or not rule.get("condition"):
        passed = payload.get("passed", True)
        plan.verify_status = "passed" if passed else "failed"
        plan.verify_result = "pass" if passed else "fail"
        plan.verify_log = (plan.verify_log or []) + [{
            "timestamp": datetime.now().isoformat(),
            "passed": passed,
            "note": note,
            "source": source,
        }]
        plan.verified_at = datetime.now()
        db.commit()
        return {"plan_id": plan_id, "verify_status": plan.verify_status, "passed": passed}

    # 2. 缺省actual → 取KPI最新值（缺陷3修复：按 period <= 当前月过滤，跨月验证不取历史期间；支持调用方显式传 period 覆盖，默认当前月）
    if actual is None:
        kpi = None
        kpi_code = rule.get("kpi_code")
        if plan.kpi_id:
            kpi = db.query(KPIDefinition).filter(KPIDefinition.id == plan.kpi_id).first()
        elif kpi_code:
            # 缺陷2修复：kpi_code 查询带 entity_id 过滤（防跨租户误匹配）
            q = db.query(KPIDefinition).filter(KPIDefinition.kpi_code == kpi_code)
            if plan.kpi_id:
                pkpi = db.query(KPIDefinition).filter(KPIDefinition.id == plan.kpi_id).first()
                if pkpi and pkpi.entity_id is not None:
                    q = q.filter(KPIDefinition.entity_id == pkpi.entity_id)
            kpi = q.first()
        if kpi:
            period_limit = payload.get("period") or datetime.now().strftime("%Y-%m")
            latest = db.query(KPIValue).filter(
                KPIValue.kpi_id == kpi.id,
                KPIValue.actual_value.isnot(None),
                KPIValue.period <= period_limit,  # 缺陷3修复：只取当前月及之前的期间
            ).order_by(KPIValue.period.desc(), KPIValue.calculated_at.desc(), KPIValue.id.desc()).first()
            if latest:
                actual = latest.actual_value

    # 3. 执行条件判断
    passed = evaluate_rule(rule, actual, kpi_data={"value": actual, "target": rule.get("target_value"), "baseline": rule.get("baseline_value")})

    # 4. 回写KPI当前值
    backfill = backfill_kpi_value(db, plan, actual, rule, source=source)

    # 缺陷1修复：调用前先记录 plan 是否已处于"验证通过"状态（防止重复累加 OKR progress）
    already_verified = bool(plan.verify_status == "passed" and plan.verified_at is not None)
    force_recalc = bool(payload.get("force_recalc", False))

    # 5. 更新状态
    plan.verify_status = "passed" if passed else "failed"
    plan.verify_result = "pass" if passed else "fail"
    plan.verify_log = (plan.verify_log or []) + [{
        "timestamp": datetime.now().isoformat(),
        "condition": rule.get("condition"),
        "actual_value": actual,
        "target_value": rule.get("target_value"),
        "passed": passed,
        "source": source,
        "note": note,
    }]
    if passed:
        plan.status = "completed"  # 缺陷4修复："done" 不在枚举(pending/in_progress/completed/cancelled)，改 completed
        plan.progress = 100
    plan.verified_at = datetime.now()

    # 6. OKR进度联动（验证通过 → +15%；缺陷1修复：已通过过的 plan 不再重复累加，force_recalc 可强制重算）
    okr_update = None
    if passed:
        okr_update = update_okr_progress(db, plan, already_verified=already_verified, force_recalc=force_recalc)

    db.commit()

    # 7. 通知任总
    if rule.get("notify", True):
        delta = ""
        if plan.kpi_current_before is not None and actual is not None:
            try:
                d = float(actual) - float(plan.kpi_current_before)
                delta = f"（{d:+.1f}）"
            except (TypeError, ValueError):
                delta = ""
        title = f"✅ 行动计划#{plan.id}验证通过" if passed else f"❌ 行动计划#{plan.id}验证失败"
        content = (
            f"{plan.title}\n"
            f"KPI: {rule.get('kpi_code', '')} {plan.kpi_current_before}→{actual} {delta}\n"
            f"规则: {rule.get('condition')} {rule.get('target_value')}\n"
            f"来源: {source}\n"
            f"{note}"
        )
        try:
            send_wecom_message(content=content, title=title, alert_level="green" if passed else "red")
        except Exception as e:
            logger.warning(f"验证通知发送失败: {e}")

    return {
        "plan_id": plan_id,
        "verify_status": plan.verify_status,
        "kpi_current_before": plan.kpi_current_before,
        "kpi_current_after": plan.kpi_current_after,
        "improvement": f"{float(actual) - float(plan.kpi_current_before):+.1f}" if plan.kpi_current_before is not None and actual is not None else None,
        "passed": passed,
        "okr_progress": okr_update,
        "kpi_backfill": backfill,
        "message": (
            f"验证通过：{plan.title}" if passed
            else f"验证失败：{plan.title}（条件 {rule.get('condition')} {rule.get('target_value')} 未达成，当前值 {actual}）"
        ),
    }
