"""KPI因果链建模 — 任务2
KPI间因果关系网络 + 模拟推演 + 三层验证机制(数据/AI/人工) (2026-08-27 P2)
"""
from datetime import datetime
from fastapi import APIRouter, Depends, HTTPException, Query
from sqlalchemy.orm import Session
from sqlalchemy import text
from typing import Optional
import logging

from app.database import get_db
from app.deps import get_entity_id
from app.auth_middleware import require_auth, require_role
from app.models import KPIDefinition, KPICausality, KPIValue, OperationLog
from app.services.causality_verification import (
    ALL_STATUSES,
    ALL_SOURCE_TYPES,
    STATUS_PENDING,
)

logger = logging.getLogger("kpi-causality")

router = APIRouter(prefix="/api/cma/kpi-causality", tags=["KPI因果链"],
    dependencies=[Depends(require_role("ceo", "finance", "business", "it"))],
)
WRITE_ROLES = Depends(require_role("ceo", "finance", "it"))


def _to_dict(obj):
    return {c.name: getattr(obj, c.name) for c in obj.__table__.columns}


# ============================================================
# 注意: 静态路径必须放在动态路径之前（/{id}之前）
# ============================================================

@router.get("/full-network")
def get_full_network(db: Session = Depends(get_db), entity_id: int = Depends(get_entity_id)):
    """获取全局因果网络数据（用于力导向图）— 账套隔离: 仅当前企业KPI的因果链 (2026-08-23 P1b, 2026-08-27 用entity_id列)"""
    edges = db.query(KPICausality).filter(KPICausality.entity_id == entity_id).all()
    node_ids = set()
    edge_list = []
    for e in edges:
        node_ids.add(e.source_kpi_id)
        node_ids.add(e.target_kpi_id)
        edge_list.append({
            "source": e.source_kpi_id,
            "target": e.target_kpi_id,
            "strength": e.strength,
            "direction": e.direction,
            "lag_months": e.lag_months,
        })

    # 获取所有节点信息
    kpis = db.query(KPIDefinition).filter(KPIDefinition.id.in_(node_ids)).all() if node_ids else []
    node_map = {k.id: {
        "id": k.id, "kpi_code": k.kpi_code, "kpi_name": k.kpi_name,
        "dimension": k.dimension, "category": k.category,
    } for k in kpis}

    nodes = []
    for nid in node_ids:
        info = node_map.get(nid, {"id": nid, "kpi_code": f"KPI#{nid}", "kpi_name": f"KPI#{nid}"})
        nodes.append(info)

    return {"nodes": nodes, "edges": edge_list, "total_edges": len(edge_list)}


@router.get("/kpi/{kpi_id}/network")
def get_kpi_network(kpi_id: int, db: Session = Depends(get_db), entity_id: int = Depends(get_entity_id)):
    """获取KPI的因果网络（上游驱动 + 下游影响）— 账套隔离: 校验KPI属于当前企业"""
    kpi = db.query(KPIDefinition).filter(KPIDefinition.id == kpi_id).first()
    if not kpi:
        raise HTTPException(404, "KPI不存在")
    if kpi.entity_id != entity_id:
        raise HTTPException(404, "KPI不存在")  # 跨企业不暴露存在性

    # 上游（指向当前KPI的因果）
    upstream = db.query(KPICausality).filter(
        KPICausality.target_kpi_id == kpi_id,
        KPICausality.entity_id == entity_id,
    ).all()
    upstream_list = []
    for c in upstream:
        src = db.query(KPIDefinition).filter(KPIDefinition.id == c.source_kpi_id).first()
        if src:
            upstream_list.append({
                "causality_id": c.id,
                "kpi_id": src.id, "kpi_code": src.kpi_code, "kpi_name": src.kpi_name,
                "strength": c.strength, "lag_months": c.lag_months,
                "direction": c.direction, "formula": c.formula,
                "source_type": c.source_type, "verify_status": c.verify_status,
                "verified_at": c.verified_at.isoformat() if c.verified_at else None,
                "verified_by": c.verified_by,
            })

    # 下游（当前KPI指向的因果）
    downstream = db.query(KPICausality).filter(
        KPICausality.source_kpi_id == kpi_id,
        KPICausality.entity_id == entity_id,
    ).all()
    downstream_list = []
    for c in downstream:
        tgt = db.query(KPIDefinition).filter(KPIDefinition.id == c.target_kpi_id).first()
        if tgt:
            downstream_list.append({
                "causality_id": c.id,
                "kpi_id": tgt.id, "kpi_code": tgt.kpi_code, "kpi_name": tgt.kpi_name,
                "strength": c.strength, "lag_months": c.lag_months,
                "direction": c.direction, "formula": c.formula,
                "source_type": c.source_type, "verify_status": c.verify_status,
                "verified_at": c.verified_at.isoformat() if c.verified_at else None,
                "verified_by": c.verified_by,
            })

    return {
        "kpi": {"id": kpi.id, "kpi_code": kpi.kpi_code, "kpi_name": kpi.kpi_name, "dimension": kpi.dimension},
        "upstream": upstream_list,
        "downstream": downstream_list,
    }


@router.post("/simulate")
def simulate_causality(data: dict, db: Session = Depends(get_db), entity_id: int = Depends(get_entity_id)):
    """模拟推演: 修改一个KPI的值，预测对其他KPI的影响
    Body: { kpi_id: int, new_value: float, period: str }
    """
    kpi_id = data.get("kpi_id")
    new_value = data.get("new_value")
    period = data.get("period")

    if not kpi_id or new_value is None:
        raise HTTPException(400, "必须指定kpi_id和new_value")

    source_kpi = db.query(KPIDefinition).filter(KPIDefinition.id == kpi_id).first()
    if not source_kpi:
        raise HTTPException(404, "KPI不存在")
    if source_kpi.entity_id != entity_id:
        raise HTTPException(404, "KPI不存在")

    # 获取当前值
    current_value = None
    query_values = db.query(KPIValue).filter(
        KPIValue.kpi_id == kpi_id,
        KPIValue.actual_value.isnot(None),
    )
    if period:
        query_values = query_values.filter(KPIValue.period == period)
    latest = query_values.order_by(KPIValue.period.desc()).first()
    if latest:
        current_value = latest.actual_value

    previous_value = current_value or new_value
    change_pct = ((new_value - previous_value) / previous_value * 100) if previous_value and previous_value != 0 else 0

    # BFS遍历下游因果链
    visited = set()
    impacts = []
    queue = [(kpi_id, change_pct, 0, 1.0)]  # (kpi_id, change_pct, depth, cumulative_strength)

    while queue:
        current_kpi_id, current_change, depth, cum_strength = queue.pop(0)
        if current_kpi_id in visited:
            continue
        visited.add(current_kpi_id)

        # 查找从current_kpi_id出发的下游因果链（账套隔离: 仅本企业链）
        downstream = db.query(KPICausality).filter(
            KPICausality.source_kpi_id == current_kpi_id,
            KPICausality.entity_id == entity_id,
        ).all()

        for edge in downstream:
            target_id = edge.target_kpi_id
            if target_id in visited:
                continue
            target_kpi = db.query(KPIDefinition).filter(KPIDefinition.id == target_id).first()
            if not target_kpi:
                continue

            # 计算影响: 变化率 × 强度 × 方向
            edge_strength = edge.strength or 0.5
            direction_factor = 1.0 if edge.direction == "positive" else -1.0
            propagated_change = current_change * edge_strength * direction_factor

            # 获取当前值
            tgt_val = db.query(KPIValue).filter(
                KPIValue.kpi_id == target_id,
                KPIValue.actual_value.isnot(None),
            ).order_by(KPIValue.period.desc()).first()

            predicted_value = None
            if tgt_val and tgt_val.actual_value:
                predicted_value = round(tgt_val.actual_value * (1 + propagated_change / 100), 2)

            impacts.append({
                "kpi_id": target_id,
                "kpi_code": target_kpi.kpi_code,
                "kpi_name": target_kpi.kpi_name,
                "dimension": target_kpi.dimension,
                "current_value": tgt_val.actual_value if tgt_val else None,
                "predicted_value": predicted_value,
                "change_pct": round(propagated_change, 2),
                "strength": edge_strength,
                "direction": edge.direction,
                "lag_months": edge.lag_months,
                "depth": depth + 1,
                "path_strength": round(cum_strength * edge_strength, 3),
            })

            # 继续遍历下游
            new_cum = cum_strength * edge_strength
            if new_cum > 0.05 and depth < 5:
                queue.append((target_id, propagated_change, depth + 1, new_cum))

    return {
        "source": {
            "kpi_id": source_kpi.id,
            "kpi_code": source_kpi.kpi_code,
            "kpi_name": source_kpi.kpi_name,
            "current_value": current_value,
            "new_value": new_value,
            "change_pct": round(change_pct, 2),
        },
        "impacts": impacts,
        "total_impacted": len(impacts),
    }


# ============================================================
# CRUD (动态路径)
# ============================================================

@router.get("")
def list_causalities(
    source_kpi_id: Optional[int] = None,
    target_kpi_id: Optional[int] = None,
    verify_status: Optional[str] = None,
    db: Session = Depends(get_db),
    entity_id: int = Depends(get_entity_id),
):
    """获取因果链列表（账套隔离: 仅当前企业KPI, 2026-08-23 P1b, 2026-08-27 支持verify_status筛选）"""
    query = db.query(KPICausality).filter(KPICausality.entity_id == entity_id)
    if source_kpi_id:
        query = query.filter(KPICausality.source_kpi_id == source_kpi_id)
    if target_kpi_id:
        query = query.filter(KPICausality.target_kpi_id == target_kpi_id)
    if verify_status:
        query = query.filter(KPICausality.verify_status == verify_status)
    items = query.order_by(KPICausality.id).all()

    result = []
    for c in items:
        d = _to_dict(c)
        src = db.query(KPIDefinition).filter(KPIDefinition.id == c.source_kpi_id).first()
        tgt = db.query(KPIDefinition).filter(KPIDefinition.id == c.target_kpi_id).first()
        d["source_kpi_code"] = src.kpi_code if src else None
        d["source_kpi_name"] = src.kpi_name if src else None
        d["target_kpi_code"] = tgt.kpi_code if tgt else None
        d["target_kpi_name"] = tgt.kpi_name if tgt else None
        result.append(d)
    return {"data": result, "total": len(result)}


@router.get("/verify-status")
def get_verify_status(
    verify_status: Optional[str] = None,
    db: Session = Depends(get_db),
    entity_id: int = Depends(get_entity_id),
):
    """验证状态总览 — 按状态统计 + 链列表（2026-08-27 三层验证机制）

    可选 ?verify_status=pending/data_verified/human_verified/disputed 筛选
    """
    if verify_status and verify_status not in ALL_STATUSES:
        raise HTTPException(400, f"verify_status 必须为 {'/'.join(ALL_STATUSES)}")

    query = db.query(KPICausality).filter(KPICausality.entity_id == entity_id)
    if verify_status:
        query = query.filter(KPICausality.verify_status == verify_status)
    items = query.order_by(KPICausality.id).all()

    by_status = {s: 0 for s in ALL_STATUSES}
    data = []
    for c in items:
        by_status[c.verify_status] = by_status.get(c.verify_status, 0) + 1
        d = _to_dict(c)
        src = db.query(KPIDefinition).filter(KPIDefinition.id == c.source_kpi_id).first()
        tgt = db.query(KPIDefinition).filter(KPIDefinition.id == c.target_kpi_id).first()
        d["source_kpi_code"] = src.kpi_code if src else None
        d["source_kpi_name"] = src.kpi_name if src else None
        d["target_kpi_code"] = tgt.kpi_code if tgt else None
        d["target_kpi_name"] = tgt.kpi_name if tgt else None
        d["verified_at"] = c.verified_at.isoformat() if c.verified_at else None
        data.append(d)

    return {
        "summary": {"total": len(items), "by_status": by_status},
        "data": data,
    }


@router.get("/{causality_id}")
def get_causality(causality_id: int, db: Session = Depends(get_db), entity_id: int = Depends(get_entity_id)):
    c = db.query(KPICausality).filter(KPICausality.id == causality_id).first()
    if not c:
        raise HTTPException(404, "因果链不存在")
    if c.entity_id != entity_id:
        raise HTTPException(404, "因果链不存在")  # 账套隔离
    d = _to_dict(c)
    src = db.query(KPIDefinition).filter(KPIDefinition.id == c.source_kpi_id).first()
    tgt = db.query(KPIDefinition).filter(KPIDefinition.id == c.target_kpi_id).first()
    d["source"] = {"id": src.id, "kpi_code": src.kpi_code, "kpi_name": src.kpi_name} if src else None
    d["target"] = {"id": tgt.id, "kpi_code": tgt.kpi_code, "kpi_name": tgt.kpi_name} if tgt else None
    return d


@router.post("")
def create_causality(data: dict, db: Session = Depends(get_db), user=WRITE_ROLES):
    """创建因果链（source_type标记来源, 2026-08-27）"""
    source_id = data.get("source_kpi_id")
    target_id = data.get("target_kpi_id")
    if not source_id or not target_id:
        raise HTTPException(400, "必须指定源KPI和目标KPI")
    if source_id == target_id:
        raise HTTPException(400, "源和目标不能相同")
    src = db.query(KPIDefinition).filter(KPIDefinition.id == source_id).first()
    tgt = db.query(KPIDefinition).filter(KPIDefinition.id == target_id).first()
    if not src or not tgt:
        raise HTTPException(404, "KPI不存在")
    if src.entity_id != tgt.entity_id:
        raise HTTPException(400, "源KPI和目标KPI必须属于同一企业")

    existing = db.query(KPICausality).filter(
        KPICausality.source_kpi_id == source_id,
        KPICausality.target_kpi_id == target_id,
    ).first()
    if existing:
        raise HTTPException(400, f"因果链已存在: {src.kpi_code}→{tgt.kpi_code}")

    source_type = data.get("source_type", "manual")
    if source_type not in ALL_SOURCE_TYPES:
        raise HTTPException(400, f"source_type 必须为 {'/'.join(ALL_SOURCE_TYPES)}")

    c = KPICausality(
        entity_id=src.entity_id,
        source_kpi_id=source_id,
        target_kpi_id=target_id,
        strength=data.get("strength", 0.5),
        lag_months=data.get("lag_months", 1),
        formula=data.get("formula"),
        direction=data.get("direction", "positive"),
        source_type=source_type,
        verify_status=STATUS_PENDING,
    )
    db.add(c)
    db.commit()
    db.refresh(c)
    db.add(OperationLog(action="create", target_type="kpi_causality",
        detail=f"创建因果链: {src.kpi_code}→{tgt.kpi_code} (source={source_type})"))
    db.commit()
    return _to_dict(c)


@router.put("/{causality_id}")
def update_causality(causality_id: int, data: dict, db: Session = Depends(get_db), user=WRITE_ROLES,
                     entity_id: int = Depends(get_entity_id)):
    c = db.query(KPICausality).filter(KPICausality.id == causality_id).first()
    if not c:
        raise HTTPException(404, "因果链不存在")
    if c.entity_id != entity_id:
        raise HTTPException(404, "因果链不存在")  # 账套隔离
    for field in ("strength", "lag_months", "formula", "direction", "source_type"):
        if field in data:
            if field == "source_type" and data[field] not in ALL_SOURCE_TYPES:
                raise HTTPException(400, f"source_type 必须为 {'/'.join(ALL_SOURCE_TYPES)}")
            setattr(c, field, data[field])
    # 修改链定义后，验证状态回到待检（定义变了旧结论失效）
    if any(f in data for f in ("strength", "lag_months", "formula", "direction")):
        c.verify_status = STATUS_PENDING
        c.verified_at = None
        c.verified_by = None
    db.commit()
    db.refresh(c)
    return _to_dict(c)


@router.put("/{causality_id}/verify")
def verify_causality(causality_id: int, data: dict, db: Session = Depends(get_db), user=WRITE_ROLES,
                     entity_id: int = Depends(get_entity_id)):
    """人工确认（战略回顾会核对打标）— 2026-08-27 三层验证机制

    Body: { verify_status: "human_verified"|"disputed", verified_by?: "任富海" }
    默认打标 human_verified（人工最终确认）。
    """
    c = db.query(KPICausality).filter(KPICausality.id == causality_id).first()
    if not c:
        raise HTTPException(404, "因果链不存在")
    if c.entity_id != entity_id:
        raise HTTPException(404, "因果链不存在")  # 账套隔离

    target_status = data.get("verify_status", "human_verified")
    if target_status not in ("human_verified", "disputed"):
        raise HTTPException(400, "verify_status 必须为 human_verified 或 disputed")
    verified_by = data.get("verified_by") or user.name or user.username

    c.verify_status = target_status
    c.verified_at = datetime.now()
    c.verified_by = str(verified_by)[:50]
    db.add(OperationLog(action="verify", target_type="kpi_causality",
        detail=f"因果链 #{causality_id} 人工确认: {target_status} (by {verified_by})"))
    db.commit()
    db.refresh(c)
    d = _to_dict(c)
    d["verified_at"] = c.verified_at.isoformat() if c.verified_at else None
    return d


@router.delete("/{causality_id}")
def delete_causality(causality_id: int, db: Session = Depends(get_db), user=WRITE_ROLES,
                     entity_id: int = Depends(get_entity_id)):
    c = db.query(KPICausality).filter(KPICausality.id == causality_id).first()
    if c:
        if c.entity_id != entity_id:
            raise HTTPException(404, "因果链不存在")  # 账套隔离
        db.delete(c)
        db.commit()
    return {"message": "已删除"}
