"""预测模拟引擎 — 管理会计OS
CVP本量利分析、投资决策(NPV/IRR)、敏感性分析、情景模拟
"""
import math
import logging
from typing import List, Dict, Optional, Tuple

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


# ============================================================
# CVP 本量利分析
# ============================================================

def cvp_analysis(
    unit_price: float,         # 单价
    unit_variable_cost: float, # 单位变动成本
    fixed_cost: float,         # 固定成本
    target_profit: float = None,  # 目标利润（可选）
    actual_volume: float = None,  # 实际销量（可选）
) -> dict:
    """CVP本量利分析

    返回：盈亏平衡点、安全边际、目标利润所需销量
    """
    if unit_price <= unit_variable_cost:
        return {"error": "单价必须大于单位变动成本"}

    contribution_margin = unit_price - unit_variable_cost  # 单位边际贡献
    contribution_ratio = round(contribution_margin / unit_price * 100, 2)  # 边际贡献率

    # 盈亏平衡点（保本点）
    bep_units = round(fixed_cost / contribution_margin, 2)  # 保本销量
    bep_revenue = round(bep_units * unit_price, 2)  # 保本销售额

    result = {
        "unit_price": unit_price,
        "unit_variable_cost": unit_variable_cost,
        "fixed_cost": fixed_cost,
        "contribution_margin": round(contribution_margin, 2),
        "contribution_ratio": contribution_ratio,
        "bep_units": bep_units,
        "bep_revenue": bep_revenue,
    }

    # 安全边际
    if actual_volume is not None:
        safety_margin_units = actual_volume - bep_units
        safety_margin_ratio = round(safety_margin_units / actual_volume * 100, 2) if actual_volume > 0 else 0
        actual_profit = round((unit_price - unit_variable_cost) * actual_volume - fixed_cost, 2)
        result["safety_margin_units"] = round(safety_margin_units, 2)
        result["safety_margin_revenue"] = round(safety_margin_units * unit_price, 2)
        result["safety_margin_ratio"] = safety_margin_ratio
        result["actual_profit"] = actual_profit

    # 目标利润
    if target_profit is not None:
        target_units = round((fixed_cost + target_profit) / contribution_margin, 2)
        target_revenue = round(target_units * unit_price, 2)
        result["target_profit"] = target_profit
        result["target_units"] = target_units
        result["target_revenue"] = target_revenue

    return result


# ============================================================
# 投资决策模型
# ============================================================

def npv(initial_investment: float, cash_flows: List[float], discount_rate: float) -> dict:
    """计算净现值 NPV = Σ CFt / (1+r)^t - I0"""
    if not cash_flows:
        return {"error": "现金流列表不能为空"}
    r = discount_rate / 100
    pv = 0
    for t, cf in enumerate(cash_flows, 1):
        pv += cf / ((1 + r) ** t)
    npv_value = round(pv - initial_investment, 2)

    # 盈利能力指数 PI = PV / I0
    pi = round(pv / initial_investment, 4) if initial_investment > 0 else 0

    return {
        "initial_investment": initial_investment,
        "discount_rate": discount_rate,
        "pv_of_cash_flows": round(pv, 2),
        "npv": npv_value,
        "profitability_index": pi,
        "is_viable": npv_value > 0,
    }


def irr(initial_investment: float, cash_flows: List[float], max_iter: int = 1000, tolerance: float = 1e-6) -> dict:
    """计算内部收益率 IRR（迭代法）"""
    if not cash_flows:
        return {"error": "现金流列表不能为空"}

    # 确保现金流总和 > 初始投资（否则 IRR 可能为负）
    total_cf = sum(cash_flows)
    if total_cf <= initial_investment:
        # 用牛顿法尝试求负IRR
        pass

    def _npv_at(rate: float) -> float:
        return sum(cf / ((1 + rate) ** (t + 1)) for t, cf in enumerate(cash_flows)) - initial_investment

    # 牛顿法求根
    rate = 0.1  # 初始猜测 10%
    for _ in range(max_iter):
        f = _npv_at(rate)
        if abs(f) < tolerance:
            break
        # 导数近似
        h = 1e-4
        df = (_npv_at(rate + h) - _npv_at(rate - h)) / (2 * h)
        if abs(df) < tolerance:
            break
        rate -= f / df
        if rate < -0.99:  # IRR 不能低于 -99%
            rate = -0.99
            break

    irr_value = round(rate * 100, 2)

    # 回收期
    cumulative = 0
    payback_period = None
    for t, cf in enumerate(cash_flows, 1):
        cumulative += cf
        if cumulative >= initial_investment:
            payback_period = t
            break

    # 动态回收期（折现）
    r = irr_value / 100 if irr_value > 0 else 0.1
    discounted_cumulative = 0
    discounted_payback = None
    for t, cf in enumerate(cash_flows, 1):
        discounted_cumulative += cf / ((1 + r) ** t)
        if discounted_cumulative >= initial_investment:
            discounted_payback = t
            break

    return {
        "initial_investment": initial_investment,
        "cash_flows": cash_flows,
        "irr": irr_value,
        "payback_period": payback_period,           # 静态回收期（年）
        "discounted_payback_period": discounted_payback,  # 动态回收期
        "is_viable": irr_value > 0,
    }


# ============================================================
# 敏感性分析
# ============================================================

def sensitivity_analysis(
    base_revenue: float,          # 基准收入
    base_cost: float,             # 基准成本
    base_profit: float = None,    # 基准利润（若为None则自动 = 收入-成本）
    step: float = 5,              # 步长 %
    max_step: float = 20,         # 最大变动 %
) -> dict:
    """单因素敏感性分析

    分析销量、单价、成本变动对利润的影响
    """
    if base_profit is None:
        base_profit = base_revenue - base_cost

    factors = []
    steps = [s for s in range(-max_step, max_step + 1, step)] or [0]

    for pct in steps:
        factor = pct / 100

        # 收入变动（销量变动）
        revenue_change_profit = base_profit * (1 + factor)
        rev_sensitivity = round((revenue_change_profit - base_profit) / base_profit * 100, 2) if base_profit else 0

        # 成本变动
        cost_change_profit = base_profit - base_cost * factor
        cost_sensitivity = round((cost_change_profit - base_profit) / base_profit * 100, 2) if base_profit else 0

        # 同时变动（收入+5%同时成本+5%）
        both_profit = (base_revenue * (1 + factor)) - (base_cost * (1 + factor))
        both_sensitivity = round((both_profit - base_profit) / base_profit * 100, 2) if base_profit else 0

        factors.append({
            "change_pct": pct,
            "revenue_change_profit": round(revenue_change_profit, 2),
            "revenue_sensitivity": rev_sensitivity,
            "cost_change_profit": round(cost_change_profit, 2),
            "cost_sensitivity": cost_sensitivity,
            "both_change_profit": round(both_profit, 2),
            "both_sensitivity": both_sensitivity,
        })

    return {
        "base_revenue": base_revenue,
        "base_cost": base_cost,
        "base_profit": round(base_profit, 2),
        "step": step,
        "max_step": max_step,
        "factors": factors,
    }


# ============================================================
# 情景模拟
# ============================================================

def scenario_analysis(
    optimistic: dict,    # {"revenue": 130, "cost": 90}
    pessimistic: dict,   # {"revenue": 80, "cost": 110}
    base: dict,          # {"revenue": 100, "cost": 100}
) -> dict:
    """三情景模拟（乐观/中性/悲观）

    每个情景包含 revenue(收入) 和 cost(成本)
    计算各情景下的利润和偏差
    """
    scenarios = []
    for label, data in [("乐观", optimistic), ("中性", base), ("悲观", pessimistic)]:
        revenue = data.get("revenue", 0)
        cost = data.get("cost", 0)
        profit = round(revenue - cost, 2)
        scenarios.append({
            "scenario": label,
            "revenue": revenue,
            "cost": cost,
            "profit": profit,
            "profit_margin": round(profit / revenue * 100, 2) if revenue else 0,
        })

    base_profit = scenarios[1]["profit"]  # 中性情景利润
    for s in scenarios:
        if base_profit:
            s["deviation_from_base"] = round(s["profit"] - base_profit, 2)
            s["deviation_pct"] = round((s["profit"] - base_profit) / base_profit * 100, 2)
        else:
            s["deviation_from_base"] = s["profit"]
            s["deviation_pct"] = 0

    # 最好/最坏/期望值（假设各1/3概率）
    expected_profit = round(
        (scenarios[0]["profit"] + scenarios[1]["profit"] + scenarios[2]["profit"]) / 3, 2
    )
    variance = sum((s["profit"] - expected_profit) ** 2 for s in scenarios) / 3
    std_dev = round(math.sqrt(variance), 2)

    return {
        "scenarios": scenarios,
        "expected_profit": expected_profit,
        "std_deviation": std_dev,
        "best_case": scenarios[0],
        "worst_case": scenarios[2],
    }
