"""预测模拟API — 管理会计OS"""
import logging
from fastapi import APIRouter, HTTPException, Depends, Request, Query
from app.utils.predict_engine import (
    cvp_analysis, npv, irr,
    sensitivity_analysis, scenario_analysis,
)
from app.utils.cash_forecast_engine import (
    forecast_cash_flow, save_forecast_to_db,
    calculate_accuracy, generate_scenario_suggestion,
)
from app.database import get_db
from app.deps import get_entity_id, resolve_entity_for_request
from sqlalchemy.orm import Session

logger = logging.getLogger("cma.predict")
router = APIRouter(prefix="/api/cma/predict", tags=["预测模拟"])


@router.post("/cvp")
def api_cvp_analysis(data: dict):
    """CVP本量利分析"""
    try:
        result = cvp_analysis(
            unit_price=float(data.get("unit_price", 0)),
            unit_variable_cost=float(data.get("unit_variable_cost", 0)),
            fixed_cost=float(data.get("fixed_cost", 0)),
            target_profit=float(data["target_profit"]) if data.get("target_profit") else None,
            actual_volume=float(data["actual_volume"]) if data.get("actual_volume") else None,
        )
        return result
    except Exception as e:
        raise HTTPException(400, f"CVP计算失败: {str(e)}")


@router.post("/investment")
def api_investment_analysis(data: dict):
    """投资决策分析（NPV/IRR/回收期）"""
    try:
        initial = float(data.get("initial_investment", 0))
        rate = float(data.get("discount_rate", 10))
        cash_flows = [float(cf) for cf in data.get("cash_flows", [])]

        if not cash_flows:
            raise HTTPException(400, "现金流列表不能为空")

        npv_result = npv(initial, cash_flows, rate)
        irr_result = irr(initial, cash_flows)

        return {
            "npv_analysis": npv_result,
            "irr_analysis": irr_result,
        }
    except HTTPException:
        raise
    except Exception as e:
        raise HTTPException(400, f"投资决策计算失败: {str(e)}")


@router.post("/sensitivity")
def api_sensitivity_analysis(data: dict):
    """敏感性分析"""
    try:
        result = sensitivity_analysis(
            base_revenue=float(data.get("base_revenue", 0)),
            base_cost=float(data.get("base_cost", 0)),
            base_profit=float(data["base_profit"]) if data.get("base_profit") else None,
            step=int(data.get("step", 5)),
            max_step=int(data.get("max_step", 20)),
        )
        return result
    except Exception as e:
        raise HTTPException(400, f"敏感性分析失败: {str(e)}")


@router.post("/scenario")
def api_scenario_analysis(data: dict):
    """情景模拟"""
    try:
        optimistic = data.get("optimistic", {})
        pessimistic = data.get("pessimistic", {})
        base = data.get("base", {})

        if not all([optimistic, pessimistic, base]):
            raise HTTPException(400, "需要提供乐观/中性/悲观三个情景的参数")

        result = scenario_analysis(
            optimistic={
                "revenue": float(optimistic.get("revenue", 0)),
                "cost": float(optimistic.get("cost", 0)),
            },
            pessimistic={
                "revenue": float(pessimistic.get("revenue", 0)),
                "cost": float(pessimistic.get("cost", 0)),
            },
            base={
                "revenue": float(base.get("revenue", 0)),
                "cost": float(base.get("cost", 0)),
            },
        )
        return result
    except HTTPException:
        raise
    except Exception as e:
        raise HTTPException(400, f"情景模拟失败: {str(e)}")


@router.post("/cvp-detailed")
def api_cvp_detailed(data: dict):
    """CVP本量利详细分析 — 含改善方案推演和保本图数据 (CMA P2)"""
    try:
        fixed_cost = float(data.get("fixed_cost", 617))
        variable_cost_rate = float(data.get("variable_cost_rate", 0.4862))
        unit_price = float(data.get("unit_price", 228))
        current_volume = float(data.get("current_volume", 5300))

        contribution_margin_rate = 1 - variable_cost_rate
        breakeven_revenue = round(fixed_cost / contribution_margin_rate, 2)
        breakeven_units = round(breakeven_revenue * 10000 / unit_price, 0)

        current_revenue = round(current_volume * unit_price / 10000, 2)
        current_profit = round(current_revenue * (1 - variable_cost_rate) - fixed_cost, 2)
        safety_margin = round((current_revenue - breakeven_revenue) / current_revenue * 100, 2) if current_revenue > 0 else 0

        scenarios = [
            {"name": "降固定费用至300万", "fixed_cost": 300, "variable_cost_rate": variable_cost_rate,
             "breakeven_revenue": round(300 / contribution_margin_rate, 2),
             "breakeven_units": round(300 / contribution_margin_rate * 10000 / unit_price, 0)},
            {"name": "降变动成本率至30%", "fixed_cost": fixed_cost, "variable_cost_rate": 0.3,
             "breakeven_revenue": round(fixed_cost / 0.7, 2),
             "breakeven_units": round(fixed_cost / 0.7 * 10000 / unit_price, 0)},
            {"name": "两者同时改善", "fixed_cost": 300, "variable_cost_rate": 0.3,
             "breakeven_revenue": round(300 / 0.7, 2),
             "breakeven_units": round(300 / 0.7 * 10000 / unit_price, 0)},
        ]

        # 保本图数据点
        chart_data = []
        max_volume = int(max(breakeven_units * 2, current_volume * 3))
        step = max(1, int(max_volume / 20))
        for vol in range(0, int(max_volume) + step, step):
            rev = round(vol * unit_price / 10000, 2)
            tc = round(fixed_cost + rev * variable_cost_rate, 2)
            chart_data.append({"volume": vol, "revenue": rev, "total_cost": tc, "profit": round(rev - tc, 2)})

        return {
            "fixed_cost": fixed_cost,
            "variable_cost_rate": round(variable_cost_rate * 100, 2),
            "unit_price": unit_price,
            "contribution_margin_rate": round(contribution_margin_rate * 100, 2),
            "breakeven_revenue": breakeven_revenue,
            "breakeven_units": int(breakeven_units),
            "current_revenue": current_revenue,
            "current_profit": current_profit,
            "current_volume": int(current_volume),
            "safety_margin": safety_margin,
            "scenarios": scenarios,
            "chart_data": chart_data,
        }
    except Exception as e:
        raise HTTPException(400, f"CVP详细分析失败: {str(e)}")


# ── 相关成本决策（CMA P2商业决策分析25%权重核心） ──────────────────


@router.post("/relevant-decision")
def api_relevant_decision(data: dict):
    """相关成本决策分析（CMA P2 商业决策分析核心内容）
    场景: make-or-buy自制外购 / special-order特殊订单 / product-mix产品组合
    """
    try:
        decision_type = data.get("type", "make_or_buy")

        if decision_type == "make_or_buy":
            # 自制vs外购决策
            # 相关成本 = 增量成本（只有随决策变化的成本才是相关的）
            make_var_cost = float(data.get("make_variable_cost", 0))   # 自制单位变动成本
            make_fixed = float(data.get("make_fixed_cost", 0))         # 自制新增固定成本
            buy_price = float(data.get("buy_price", 0))                # 外购单价
            demand = float(data.get("demand", 0))                      # 需求量
            existing_fixed = float(data.get("existing_fixed_cost", 0)) # 现有固定成本（无关成本，自制不增加则忽略）

            make_total = make_var_cost * demand + make_fixed
            buy_total = buy_price * demand
            diff = buy_total - make_total  # >0自制省钱

            return {
                "type": "自制vs外购",
                "make_total_cost": round(make_total, 2),
                "buy_total_cost": round(buy_total, 2),
                "difference": round(diff, 2),
                "recommendation": "自制" if diff > 0 else "外购",
                "reason": f"自制总成本{make_total:.2f} vs 外购总成本{buy_total:.2f}，{'自制节省' + str(round(diff,2)) if diff > 0 else '外购节省' + str(round(-diff,2))}",
                "unit_make_cost": round(make_var_cost + (make_fixed / demand if demand else 0), 2),
                "unit_buy_price": buy_price,
                "indifferent_point": round(make_fixed / (buy_price - make_var_cost), 2) if buy_price > make_var_cost else None,
                "notes": "仅考虑相关成本(增量成本)；现有固定成本若不受决策影响则无关",
            }

        elif decision_type == "special_order":
            # 特殊订单决策（有剩余产能时，只要价格>单位变动成本即接受）
            normal_price = float(data.get("normal_price", 0))
            special_price = float(data.get("special_price", 0))
            var_cost = float(data.get("variable_cost", 0))
            order_qty = float(data.get("order_qty", 0))
            capacity_used = float(data.get("capacity_used", 0))  # 特殊订单占用产能%
            extra_fixed = float(data.get("extra_fixed_cost", 0))  # 一次性额外固定成本

            contribution_per_unit = special_price - var_cost
            total_contribution = contribution_per_unit * order_qty - extra_fixed
            accept = total_contribution > 0 and capacity_used <= 100

            return {
                "type": "特殊订单",
                "unit_contribution": round(contribution_per_unit, 2),
                "total_contribution": round(total_contribution, 2),
                "extra_fixed_cost": extra_fixed,
                "capacity_used_pct": capacity_used,
                "recommendation": "接受" if accept else "拒绝",
                "reason": f"单价{special_price} - 变动成本{var_cost} = 单位贡献{contribution_per_unit:.2f}" + 
                          (f"，共{total_contribution:.2f} > 0 且产能{capacity_used}%够用 → 接受(增量利润)" if accept else 
                           f"，总贡献{total_contribution:.2f} ≤ 0 或产能不足 → 拒绝"),
                "notes": "有剩余产能时，只要价格>变动成本且不冲击正常市场即可接受；固定成本无关",
            }

        elif decision_type == "product_mix":
            # 产品组合决策（约束理论：单位约束资源的边际贡献最大者优先）
            products = data.get("products", [])  # [{name, price, var_cost, constraint_usage, demand}]
            results = []
            for p in products:
                cm_per_unit = float(p.get("price", 0)) - float(p.get("var_cost", 0))
                cm_per_constraint = cm_per_unit / float(p.get("constraint_usage", 1))
                results.append({
                    "name": p.get("name", ""),
                    "unit_contribution": round(cm_per_unit, 2),
                    "constraint_usage": float(p.get("constraint_usage", 1)),
                    "contribution_per_constraint": round(cm_per_constraint, 2),
                    "demand": float(p.get("demand", 0)),
                })
            # 按单位约束资源贡献排序（约束理论优先）
            results.sort(key=lambda x: x["contribution_per_constraint"], reverse=True)
            return {
                "type": "产品组合(约束理论)",
                "ranking": results,
                "recommendation": f"优先生产「{results[0]['name']}」(单位约束贡献{results[0]['contribution_per_constraint']}最高)",
                "notes": "瓶颈资源下，按单位约束资源的边际贡献排序，而非单位边际贡献",
            }

        raise HTTPException(400, "未知决策类型: " + str(decision_type))
    except Exception as e:
        raise HTTPException(400, f"相关成本决策失败: {str(e)}")


# ── 现金流预测（AI事前预警） ────────────────────────────────────


@router.post("/cash-forecast")
def api_cash_forecast(request: Request, data: dict, db: Session = Depends(get_db)):
    """现金流预测 — 根据历史KPI推算未来30天现金流"""
    try:
        entity_id = resolve_entity_for_request(request, int(data.get("entity_id", 1)))
        days = int(data.get("days", 30))
        current_cash = float(data["current_cash"]) if data.get("current_cash") else None
        result = forecast_cash_flow(entity_id, db, days, current_cash)
        # 保存到数据库
        try:
            save_forecast_to_db(entity_id, result, db)
        except Exception as e:
            logger.warning(f"保存预测结果失败: {e}")
        return result
    except Exception as e:
        raise HTTPException(400, f"现金流预测失败: {str(e)}")


@router.get("/cash-forecast/history")
def api_cash_forecast_history(
    entity_id: int = Depends(get_entity_id),
    days: int = 30,
    db: Session = Depends(get_db),
):
    """获取已保存的现金流预测历史"""
    from app.models import CashForecast
    forecasts = db.query(CashForecast).filter(
        CashForecast.entity_id == entity_id,
    ).order_by(CashForecast.forecast_date.desc()).limit(days).all()
    return {
        "data": [{
            "id": f.id,
            "forecast_date": f.forecast_date.isoformat(),
            "predicted_cash": f.predicted_cash,
            "lower_bound": f.lower_bound,
            "upper_bound": f.upper_bound,
            "alert_status": f.alert_status,
        } for f in forecasts]
    }


@router.get("/accuracy")
def api_forecast_accuracy(
    entity_id: int = Depends(get_entity_id),
    db: Session = Depends(get_db),
):
    """预测准确率报表 — 上期预测 vs 本期实际"""
    try:
        results = calculate_accuracy(entity_id, db)

        # 计算整体MAE/MAPE
        if results:
            total_mae = sum(r["mae"] for r in results) / len(results)
            total_mape = sum(r["mape"] for r in results) / len(results)
        else:
            total_mae = 0
            total_mape = 0

        return {
            "data": results,
            "summary": {
                "total_periods": len(results),
                "avg_mae": round(total_mae, 2),
                "avg_mape": round(total_mape, 2),
            },
        }
    except Exception as e:
        raise HTTPException(400, f"获取准确率失败: {str(e)}")


@router.get("/scenario-suggestions")
def api_scenario_suggestions(alert_type: str = None):
    """获取情景建议模板"""
    types = ["cash_low", "cash_critical", "cost_high", "revenue_drop"]
    results = []
    for at in types:
        if alert_type and at != alert_type:
            continue
        sug = generate_scenario_suggestion(at, "")
        results.append({"alert_type": at, **sug})
    return {"data": results}


@router.post("/scenario-suggestion/generate")
def api_generate_suggestion(data: dict):
    """根据预警信息动态生成情景建议"""
    try:
        alert_type = data.get("alert_type", "cash_low")
        kpi_name = data.get("kpi_name", "未知KPI")
        extra = data.get("extra", {})
        sug = generate_scenario_suggestion(alert_type, kpi_name, extra)
        return sug
    except Exception as e:
        raise HTTPException(400, f"生成建议失败: {str(e)}")


# ── 实物期权计算器 ─────────────────────────────────────────────
import math

def _norm_cdf(x: float) -> float:
    """标准正态分布CDF — Abramowitz & Stegun 近似 (max error ≈ 1.5×10⁻⁷)"""
    a1, a2, a3, a4, a5 = 0.254829592, -0.284496736, 1.421413741, -1.453152027, 1.061405429
    p = 0.3275911
    sign = 1.0
    if x < 0:
        sign = -1.0
    x_abs = abs(x) / math.sqrt(2.0)
    t = 1.0 / (1.0 + p * x_abs)
    y = 1.0 - (((((a5 * t + a4) * t) + a3) * t + a2) * t + a1) * t * math.exp(-x_abs * x_abs)
    return 0.5 * (1.0 + sign * y)

def _black_scholes_call(S0: float, X: float, t: float, r: float, sigma: float) -> dict:
    """BSM看涨期权定价（扩张期权/延迟期权）"""
    sqrt_t = math.sqrt(t)
    d1 = (math.log(S0 / X) + (r + 0.5 * sigma ** 2) * t) / (sigma * sqrt_t)
    d2 = d1 - sigma * sqrt_t
    nd1 = _norm_cdf(d1)
    nd2 = _norm_cdf(d2)
    call_value = max(S0 * nd1 - X * math.exp(-r * t) * nd2, 0.0)
    return {"value": round(call_value, 4), "d1": round(d1, 4), "d2": round(d2, 4), "Nd1": round(nd1, 4), "Nd2": round(nd2, 4)}

def _black_scholes_put(S0: float, X: float, t: float, r: float, sigma: float) -> dict:
    """BSM看跌期权定价（放弃期权/收缩期权）"""
    sqrt_t = math.sqrt(t)
    d1 = (math.log(S0 / X) + (r + 0.5 * sigma ** 2) * t) / (sigma * sqrt_t)
    d2 = d1 - sigma * sqrt_t
    nd1 = _norm_cdf(-d1)
    nd2 = _norm_cdf(-d2)
    put_value = max(X * math.exp(-r * t) * nd2 - S0 * nd1, 0.0)
    return {"value": round(put_value, 4), "d1": round(d1, 4), "d2": round(d2, 4), "N(-d1)": round(nd1, 4), "N(-d2)": round(nd2, 4)}

def _binomial_tree_call(S0: float, X: float, t: float, r: float, sigma: float, n: int = 100) -> float:
    """二叉树欧式看涨期权定价（延迟期权）"""
    dt = t / n
    u = math.exp(sigma * math.sqrt(dt))
    d = 1.0 / u
    p = (math.exp(r * dt) - d) / (u - d)
    discount = math.exp(-r * dt)
    prices = [S0 * (u ** (n - j)) * (d ** j) for j in range(n + 1)]
    values = [max(p - X, 0.0) for p in prices]
    for i in range(n - 1, -1, -1):
        for j in range(i + 1):
            values[j] = discount * (p * values[j] + (1 - p) * values[j + 1])
    return max(values[0], 0.0)

def _binomial_tree_american_put(S0: float, X: float, t: float, r: float, sigma: float, n: int = 100) -> float:
    """二叉树美式看跌期权定价（可随时放弃的放弃期权）"""
    dt = t / n
    u = math.exp(sigma * math.sqrt(dt))
    d = 1.0 / u
    p = (math.exp(r * dt) - d) / (u - d)
    discount = math.exp(-r * dt)
    prices = [S0 * (u ** (n - j)) * (d ** j) for j in range(n + 1)]
    values = [max(X - p, 0.0) for p in prices]
    for i in range(n - 1, -1, -1):
        for j in range(i + 1):
            hold = discount * (p * values[j] + (1 - p) * values[j + 1])
            exercise = X - (S0 * (u ** (i - j)) * (d ** j))
            values[j] = max(hold, exercise)
    return max(values[0], 0.0)

@router.post("/real-option")
def api_real_option(data: dict):
    """实物期权计算器"""
    try:
        opt_type = data.get("opt_type", "expansion")  # expansion|abandon|delay|shrink
        model = data.get("model", "bs")               # bs|binomial
        S0 = float(data.get("S0", 100.0))
        X = float(data.get("X", 80.0))
        t = float(data.get("t", 3.0))
        r = float(data.get("r", 0.0174))
        sigma = float(data.get("sigma", 0.30))
        expansion_factor = float(data.get("expansion_factor", 1.5))
        salvage_value = float(data.get("salvage_value", S0 * 0.3))
        n_steps = int(data.get("n_steps", 100))

        # 输入校验
        if S0 <= 0 or X <= 0 or t <= 0 or sigma <= 0:
            raise HTTPException(400, "参数必须为正数")
        if sigma > 2.0:
            raise HTTPException(400, "波动率σ不能超过200%")

        result = {"option_type": opt_type, "model": model, "S0": S0, "X": X, "t": t, "r": r, "sigma": sigma}

        # 计算期权价值
        if opt_type in ("expansion", "delay") and model == "bs":
            bs = _black_scholes_call(S0, X, t, r, sigma)
            result["option_value"] = bs["value"]
            result["intermediate"] = {k: v for k, v in bs.items() if k != "value"}
        elif opt_type == "expansion" and model == "binomial":
            adj_X = X / expansion_factor
            bt_val = _binomial_tree_call(S0, adj_X, t, r, sigma, n_steps)
            option_value = max(bt_val * expansion_factor, 0.0)
            result["option_value"] = round(option_value, 4)
            result["intermediate"] = {"expansion_factor": expansion_factor, "adjusted_X": round(adj_X, 4), "tree_value": round(bt_val, 4)}
        elif opt_type == "delay" and model == "binomial":
            option_value = _binomial_tree_call(S0, X, t, r, sigma, n_steps)
            result["option_value"] = round(option_value, 4)
            # Also compute BS for reference
            bs = _black_scholes_call(S0, X, t, r, sigma)
            result["intermediate"] = {"n_steps": n_steps, "bs_reference": round(bs["value"], 4)}
        elif opt_type in ("abandon", "shrink") and model == "bs":
            effective_X = salvage_value if opt_type == "abandon" else X
            bs = _black_scholes_put(S0, effective_X, t, r, sigma)
            result["option_value"] = bs["value"]
            result["intermediate"] = {k: v for k, v in bs.items() if k != "value"}
            if opt_type == "abandon":
                result["intermediate"]["salvage_value"] = effective_X
        elif opt_type == "abandon" and model == "binomial":
            bt_val = _binomial_tree_american_put(S0, salvage_value, t, r, sigma, n_steps)
            result["option_value"] = round(bt_val, 4)
            result["intermediate"] = {"n_steps": n_steps, "salvage_value": salvage_value}
        else:
            raise HTTPException(400, f"不支持的组合: {opt_type} + {model}")

        # 决策建议
        val = result["option_value"]
        if val > 0:
            result["suggestion"] = "期权价值 > 0，管理弹性有价值，建议保留决策弹性，在有利时机行权"
            result["suggestion_type"] = "positive"
        else:
            result["suggestion"] = "期权价值 ≈ 0，弹性无明显价值，建议按传统NPV决策，无需等待"
            result["suggestion_type"] = "neutral"

        # 扩展NPV（假设传统NPV = S0 - X）
        npv_without = S0 - X
        expanded_npv = npv_without + val
        result["npv_without_flexibility"] = round(npv_without, 4)
        result["expanded_npv"] = round(expanded_npv, 4)

        if expanded_npv > 0:
            result["decision"] = "✅ 扩展NPV > 0，含弹性后项目整体值得投资"
        else:
            result["decision"] = "❌ 扩展NPV ≤ 0，含弹性后项目仍不值得投资"

        # 敏感性分析数据（σ从10%~90%变化）
        sensitivity = []
        for s_pct in range(5, 96, 5):
            s = s_pct / 100.0
            if opt_type in ("expansion", "delay"):
                if model == "bs":
                    v = _black_scholes_call(S0, X, t, r, s)["value"]
                else:
                    bt = _binomial_tree_call(S0, X, t, r, s, n_steps)
                    v = bt * expansion_factor if opt_type == "expansion" else bt
            else:
                eff_X = salvage_value if opt_type == "abandon" else X
                if model == "bs":
                    v = _black_scholes_put(S0, eff_X, t, r, s)["value"]
                else:
                    v = _binomial_tree_american_put(S0, eff_X, t, r, s, n_steps)
            sensitivity.append({"sigma": s_pct, "option_value": round(v, 4)})
        result["sensitivity"] = sensitivity

        # 警告提示
        warnings = []
        if t * sigma * sigma * 0.5 > r:
            warnings.append("高波动+长时间，延迟价值显著")
        if S0 < X:
            warnings.append("价外期权，期权价值较低")
        if S0 > X * 1.5:
            warnings.append("深度价内，几乎确定行权")
        if sigma < 0.10:
            warnings.append("波动率过低，期权价值趋近于0")
        if t > 10:
            warnings.append("长期期权，贴现因子影响大")
        result["warnings"] = warnings

        return result
    except HTTPException:
        raise
    except Exception as e:
        raise HTTPException(400, f"实物期权计算失败: {str(e)}")


# ── 增长质量诊断 ─────────────────────────────────────────────

def _score_revenue_structure(entity: dict) -> int:
    """营收结构评分：渠补率越低越好"""
    rebate_rate = float(entity.get("rebateRate", entity.get("rebate_rate", 0)))
    if rebate_rate > 80: return 1
    if rebate_rate > 60: return 2
    if rebate_rate > 40: return 3
    if rebate_rate > 20: return 4
    return 5


def _score_profit_structure(entity: dict) -> int:
    """利润结构评分：真实毛利率越高越好"""
    gross_margin = float(entity.get("trueGrossMargin", entity.get("true_gross_margin", 0)))
    if gross_margin < 0: return 1
    if gross_margin < 10: return 2
    if gross_margin < 20: return 3
    if gross_margin < 30: return 4
    return 5


def _score_cash_assets(entity: dict) -> int:
    """现金资产评分：现金比率越高越好"""
    cash_ratio = float(entity.get("cashRatio", entity.get("cash_ratio", 0)))
    if cash_ratio < 10: return 1
    if cash_ratio < 30: return 2
    if cash_ratio < 50: return 3
    if cash_ratio < 100: return 4
    return 5


def _score_growth_driver(entity: dict) -> int:
    """增长驱动评分：费用增速相对收入增速越低越好"""
    expense_growth = float(entity.get("expenseGrowthRate", entity.get("expense_growth_rate", 0)))
    revenue_growth = float(entity.get("revenueGrowthRate", entity.get("revenue_growth_rate", 1)))
    if revenue_growth <= 0: revenue_growth = 1  # prevent div by zero
    ratio = expense_growth / revenue_growth
    if ratio > 1.5: return 1
    if ratio > 1.2: return 2
    if ratio > 1.0: return 3
    if ratio > 0.8: return 4
    return 5


def _score_org_efficiency(entity: dict) -> int:
    """组织效率评分：管理费/净收入越低越好"""
    mgmt_ratio = float(entity.get("mgmtRatio", entity.get("mgmt_ratio", 0)))
    if mgmt_ratio > 300: return 1
    if mgmt_ratio > 200: return 2
    if mgmt_ratio > 100: return 3
    if mgmt_ratio > 50: return 4
    return 5


def _diagnosis_text(overall: float, dimensions: dict, entity_name: str) -> str:
    """根据评分生成诊断结论"""
    lines = []
    low_dims = {k: v for k, v in dimensions.items() if v["score"] <= 2}
    mid_dims = {k: v for k, v in dimensions.items() if 2 < v["score"] < 4}

    dim_labels = {
        "revenueStructure": "营收结构",
        "profitStructure": "利润结构",
        "cashAssets": "现金资产",
        "growthDriver": "增长驱动",
        "orgEfficiency": "组织效率",
    }

    if overall < 2:
        lines.append(f"{entity_name}的增长质量评分仅{overall}分，属于「越增长越重」类型。")
        lines.append("增长主要依赖资源投入而非核心能力积累，可持续性堪忧。")
    elif overall < 3:
        lines.append(f"{entity_name}的增长质量评分{overall}分，需重点关注。")
        lines.append("部分维度存在风险，增长质量有待改善。")
    elif overall < 4:
        lines.append(f"{entity_name}的增长质量评分{overall}分，处于中等水平。")
        lines.append("多数维度表现尚可，仍有优化空间。")
    else:
        lines.append(f"{entity_name}的增长质量评分{overall}分，「越增长越轻」。")
        lines.append("增长模式健康，具备持续增长能力。")

    if low_dims:
        low_names = [dim_labels.get(k, k) for k in low_dims]
        lines.append(f"⚠️ 需重点关注：{'、'.join(low_names)}评分偏低（≤2分）。")

    if mid_dims:
        mid_names = [dim_labels.get(k, k) for k in mid_dims]
        lines.append(f"💡 可优化：{'、'.join(mid_names)}有提升空间。")

    # 具体建议（硬编码的关键诊断）
    if dimensions.get("revenueStructure", {}).get("score", 5) <= 2:
        lines.append("• 营收依赖渠道返利，建议降低渠补率、拓展直销渠道。")
    if dimensions.get("orgEfficiency", {}).get("score", 5) <= 2:
        lines.append("• 管理费率高企，建议精简费用结构、优化运营效率。")
    if dimensions.get("cashAssets", {}).get("score", 5) <= 2:
        lines.append("• 现金比率极低，存在断流风险，建议加强现金流管理。")
    if dimensions.get("growthDriver", {}).get("score", 5) <= 2:
        lines.append("• 费用增速远超收入增速，增长不可持续，需控制费用膨胀。")

    return "\n".join(lines)


def _generate_improvement_suggestions(dimension: str, score: int, entity: dict) -> list:
    """为指定维度生成改善建议"""
    suggestions = []
    if dimension == "revenueStructure":
        rebate = float(entity.get("rebateRate", entity.get("rebate_rate", 0)))
        if score <= 2:
            target_rebate = max(rebate - 10, 0)
            savings = f"释放现金{round(rebate - target_rebate, 1)}%/月"
            suggestions.append(f"渠补谈判：{rebate}%→{target_rebate}%（{savings}）")
            suggestions.append("客户分散：拓展直销渠道，降低渠道依赖")
            suggestions.append("渠补制度：分级管理，差异化返利")
        else:
            suggestions.append("维持现有渠补政策")
    elif dimension == "profitStructure":
        gm = float(entity.get("trueGrossMargin", entity.get("true_gross_margin", 0)))
        if score <= 2:
            suggestions.append(f"成本优化：毛利率仅{gm}%，需分析成本构成")
            suggestions.append("产品结构：提高高毛利产品占比")
            suggestions.append("定价策略：评估提价空间")
        else:
            suggestions.append("维持毛利率水平")
    elif dimension == "cashAssets":
        cr = float(entity.get("cashRatio", entity.get("cash_ratio", 0)))
        if score <= 2:
            suggestions.append(f"现金管理：现金比率仅{cr}%，存在断流风险")
            suggestions.append("应收账款：加快回款周期")
            suggestions.append("融资安排：准备短期授信额度")
        else:
            suggestions.append("维持现金流健康")
    elif dimension == "growthDriver":
        eg = float(entity.get("expenseGrowthRate", entity.get("expense_growth_rate", 0)))
        if score <= 2:
            suggestions.append(f"费用管控：费用增速{eg}倍于收入，需严控费用")
            suggestions.append("预算管理：建立费用增长红线机制")
            suggestions.append("投资回报：评估每项投入的ROI")
        else:
            suggestions.append("维持费用增长与收入增长匹配")
    elif dimension == "orgEfficiency":
        mr = float(entity.get("mgmtRatio", entity.get("mgmt_ratio", 0)))
        if score <= 2:
            suggestions.append(f"管理效率：管理费/净收入{mr}%，急需降本增效")
            suggestions.append("组织精简：评估管理层级压缩空间")
            suggestions.append("流程优化：推进数字化降本")
        else:
            suggestions.append("维持管理效率水平")
    return suggestions


def _get_dim_detail_indicators(dimension: str, entity: dict) -> list:
    """获取维度的明细诊断指标"""
    indicators = []
    if dimension == "revenueStructure":
        rebate = float(entity.get("rebateRate", entity.get("rebate_rate", 0)))
        net_ratio = round(100 - rebate, 1)
        indicators.append({"label": "渠补率", "value": f"{rebate}%", 
                          "verdict": "收入依赖渠道返利" if rebate > 50 else "渠道依赖程度中等",
                          "status": "danger" if rebate > 50 else "warning" if rebate > 20 else "success"})
        indicators.append({"label": "净收入占比", "value": f"{net_ratio}%",
                          "verdict": f"仅{net_ratio}%归公司" if net_ratio < 30 else "净收入占比合理",
                          "status": "danger" if net_ratio < 30 else "success"})
    elif dimension == "profitStructure":
        gm = float(entity.get("trueGrossMargin", entity.get("true_gross_margin", 0)))
        indicators.append({"label": "真实毛利率", "value": f"{gm}%",
                          "verdict": "毛利偏低" if gm < 15 else "毛利正常",
                          "status": "danger" if gm < 10 else "warning" if gm < 20 else "success"})
    elif dimension == "cashAssets":
        cr = float(entity.get("cashRatio", entity.get("cash_ratio", 0)))
        indicators.append({"label": "现金比率", "value": f"{cr}%",
                          "verdict": "断流风险" if cr < 5 else "现金紧张" if cr < 30 else "现金充足",
                          "status": "danger" if cr < 5 else "warning" if cr < 30 else "success"})
    elif dimension == "growthDriver":
        eg = float(entity.get("expenseGrowthRate", entity.get("expense_growth_rate", 0)))
        rg = float(entity.get("revenueGrowthRate", entity.get("revenue_growth_rate", 1)))
        ratio = eg / rg if rg > 0 else 99
        indicators.append({"label": "费用增速/收入增速", "value": f"{ratio:.1f}倍",
                          "verdict": "费用增速过快" if ratio > 1.5 else "费用可控" if ratio > 1 else "增长健康",
                          "status": "danger" if ratio > 1.5 else "warning" if ratio > 1 else "success"})
    elif dimension == "orgEfficiency":
        mr = float(entity.get("mgmtRatio", entity.get("mgmt_ratio", 0)))
        indicators.append({"label": "管理费/净收入", "value": f"{mr}%",
                          "verdict": "管理费极高" if mr > 200 else "管理费偏高" if mr > 100 else "管理费正常",
                          "status": "danger" if mr > 200 else "warning" if mr > 100 else "success"})
    return indicators


@router.post("/growth-quality")
def api_growth_quality(request: Request, data: dict):
    """增长质量诊断 — 五维度评分+综合评分+诊断结论"""
    try:
        entity_id = resolve_entity_for_request(request, data.get("entity_id"))
        ENTITY_DATA = {
            1: {"entity":"陕西酣客文化传媒","rebateRate":82.8,"trueGrossMargin":18.6,"cashRatio":0.6,"expenseGrowthRate":2.2,"revenueGrowthRate":1.0,"mgmtRatio":447},
            2: {"entity":"陕西博海网络科技","rebateRate":0,"trueGrossMargin":13.1,"cashRatio":6.7,"expenseGrowthRate":0.8,"revenueGrowthRate":1.0,"mgmtRatio":1.4},
        }
        entity = ENTITY_DATA.get(entity_id, data.get("entity", data))
        entity_name = entity.get("entity", entity.get("name", "该企业"))
        period = entity.get("period", data.get("period", "当前"))

        # 五维度评分
        dim_scores = {
            "revenueStructure": _score_revenue_structure(entity),
            "profitStructure": _score_profit_structure(entity),
            "cashAssets": _score_cash_assets(entity),
            "growthDriver": _score_growth_driver(entity),
            "orgEfficiency": _score_org_efficiency(entity),
        }

        overall = round(sum(dim_scores.values()) / 5, 1)

        # 综合等级
        if overall >= 4:
            level = "🟢 越增长越轻"
            level_type = "excellent"
        elif overall >= 3:
            level = "🟡 增长质量中等"
            level_type = "medium"
        elif overall >= 2:
            level = "🟠 需关注"
            level_type = "warning"
        else:
            level = "🔴 越增长越重"
            level_type = "danger"

        # 诊断结论
        dimensions_payload = {}
        detail_payload = {}
        for dim, score in dim_scores.items():
            dimensions_payload[dim] = {"score": score, "weight": 20}
            detail_payload[dim] = {
                "score": score,
                "indicators": _get_dim_detail_indicators(dim, entity),
                "suggestions": _generate_improvement_suggestions(dim, score, entity),
            }

        diagnosis = _diagnosis_text(overall, dimensions_payload, entity_name)

        # 对比数据（如果请求中包含多个实体）
        compare = data.get("compare", None)
        compare_result = None
        if compare:
            compare_entity = compare
            compare_name = compare_entity.get("entity", compare_entity.get("name", "对比企业"))
            cdims = {
                "revenueStructure": _score_revenue_structure(compare_entity),
                "profitStructure": _score_profit_structure(compare_entity),
                "cashAssets": _score_cash_assets(compare_entity),
                "growthDriver": _score_growth_driver(compare_entity),
                "orgEfficiency": _score_org_efficiency(compare_entity),
            }
            compare_overall = round(sum(cdims.values()) / 5, 1)
            compare_result = {
                "entity_name": compare_name,
                "overall": compare_overall,
                "dimensions": {k: {"score": v, "weight": 20} for k, v in cdims.items()},
                "level": ("🟢 越增长越轻" if compare_overall >= 4 else
                          "🟡 增长质量中等" if compare_overall >= 3 else
                          "🟠 需关注" if compare_overall >= 2 else "🔴 越增长越重"),
            }

        return {
            "entity_name": entity_name,
            "period": period,
            "overall": overall,
            "level": level,
            "level_type": level_type,
            "dimensions": dimensions_payload,
            "detail": detail_payload,
            "diagnosis": diagnosis,
            "compare": compare_result,
        }
    except Exception as e:
        raise HTTPException(400, f"增长质量诊断失败: {str(e)}")


# ── KPI趋势预测（预测性成本智能 MVP） ────────────────────────────
from app.utils.kpi_forecast_engine import (  # noqa: E402
    MODELS, forecast_kpi, forecast_finance_kpis,
    MACRO_FACTORS, factor_sensitivity_for_kpi, factor_sensitivity_with_history,
    adjusted_next_with_factor, save_forecast_logs,
)


@router.get("/kpi-forecast")
def api_kpi_forecast(
    kpi_code: str,
    periods: int = 3,
    model: str = "linear",
    entity_id: int = Depends(get_entity_id),
    db: Session = Depends(get_db),
):
    """单个财务KPI预测 — 线性回归/移动平均，多租户隔离（entity_id 权限校验）"""
    if periods < 0 or periods > 24:
        raise HTTPException(400, "periods 必须在 0~24 之间")
    if model not in MODELS:
        raise HTTPException(400, f"不支持的模型: {model}，可选: {'/'.join(MODELS)}")
    result = forecast_kpi(entity_id, kpi_code, db, periods=periods, model=model)
    if result is None:
        raise HTTPException(
            404,
            f"KPI {kpi_code} 在企业 entity_id={entity_id} 下不存在，或历史数据不足（至少2条）",
        )
    return result


@router.get("/kpi-forecast/finance")
def api_kpi_forecast_finance(
    periods: int = 3,
    model: str = "linear",
    entity_id: int = Depends(get_entity_id),
    db: Session = Depends(get_db),
):
    """批量预测该企业全部财务维度KPI（历史≥3条），按可预测性排序"""
    if periods < 0 or periods > 24:
        raise HTTPException(400, "periods 必须在 0~24 之间")
    if model not in MODELS:
        raise HTTPException(400, f"不支持的模型: {model}，可选: {'/'.join(MODELS)}")
    results = forecast_finance_kpis(entity_id, db, periods=periods, model=model)
    try:
        save_forecast_logs(entity_id, results, db, model=model)  # 升级2a: 预测落库(供偏差告警)
    except Exception as e:
        logger.warning(f"预测落库失败(不影响返回): {e}")
    return {
        "entity_id": entity_id,
        "model": model,
        "periods": periods,
        "total": len(results),
        "data": results,
    }


@router.get("/kpi-forecast/sensitivity")
def api_kpi_forecast_sensitivity(
    pct: float = Query(10, description="宏观因素变动幅度% (±)"),
    periods: int = Query(3),
    model: str = Query("linear"),
    entity_id: int = Depends(get_entity_id),
    db: Session = Depends(get_db),
):
    """宏观敏感性因素联动（IMA 2026.7）— 财务KPI × 宏观因素(油价/汇率/CPI)敏感性矩阵
    输出：每个KPI的预测值 + 各因素 ±pct% 情景下的调整后预测值
    MVP：弹性系数为规则推断（按KPI类别），诚实标注"模型弹性"非历史回归"""
    if abs(pct) > 50:
        raise HTTPException(400, "pct 必须在 ±50 以内")
    if model not in MODELS:
        raise HTTPException(400, f"不支持的模型: {model}，可选: {'/'.join(MODELS)}")
    results = forecast_finance_kpis(entity_id, db, periods=periods, model=model)
    matrix = []
    for r in results:
        kpi_info = r.get("kpi", {})
        # v2: 有历史数据用变化率弹性校准，无数据回退规则推断
        sens = factor_sensitivity_with_history(
            kpi_info.get("name", ""), kpi_info.get("code", ""), r.get("history", []))
        next_val = r.get("next_target")
        factor_effects = []
        for s in sens:
            up_val = adjusted_next_with_factor(next_val, pct, s["direction"], s["elasticity"])
            down_val = adjusted_next_with_factor(next_val, -pct, s["direction"], s["elasticity"])
            factor_effects.append({
                "factor_key": s["factor_key"],
                "factor_name": s["factor_name"],
                "factor_unit": s["factor_unit"],
                "direction": s["direction"],
                "elasticity": s["elasticity"],
                "elasticity_source": s.get("elasticity_source", "rule"),
                "matched_periods": s.get("matched_periods"),
                "rule_direction": s.get("rule_direction"),
                "adj_up": up_val,
                "adj_down": down_val,
            })
        matrix.append({
            "kpi": kpi_info,
            "category": sens[0]["category"] if sens else "profit",
            "next_target": next_val,
            "confidence": r.get("confidence"),
            "trend": r.get("trend"),
            "factors": factor_effects,
        })
    return {
        "entity_id": entity_id,
        "model": model,
        "periods": periods,
        "pct": pct,
        "factors": MACRO_FACTORS,
        "total": len(matrix),
        "data": matrix,
    }
