"""管理会计OS 数据模型"""
from sqlalchemy import Column, Integer, String, Text, Float, DateTime, ForeignKey, Boolean, JSON, func, UniqueConstraint, Numeric, Date
from app.database import Base

from app.models.budget_plan import BudgetPlan
from app.models.cost_model import StandardCost, ActualCost, AbcActivity, AbcAllocation
from app.models.knowledge import KnowledgeEvent, KnowledgeSummary
from app.models.driver_budget import DriverFactorTemplate, DriverFactorBudget
from app.models.product_sales import ProductSales


class Entity(Base):
    """企业实体"""
    __tablename__ = "entities"
    id = Column(Integer, primary_key=True, index=True)
    name = Column(String(100), nullable=False, comment="企业全称")
    short_name = Column(String(50), comment="企业简称")
    industry = Column(String(50), comment="行业")
    status = Column(String(20), default="active", comment="active/inactive/demo")
    created_at = Column(DateTime, server_default=func.now())
    updated_at = Column(DateTime, server_default=func.now(), onupdate=func.now())


class User(Base):
    """用户"""
    __tablename__ = "users"
    id = Column(Integer, primary_key=True, index=True)
    username = Column(String(50), unique=True, nullable=False)
    password_hash = Column(String(128), nullable=False)
    name = Column(String(100), nullable=False)
    role = Column(String(20), default="finance")  # ceo / finance / business / it
    phone = Column(String(20), nullable=True)
    created_at = Column(DateTime, server_default=func.now())


class UserEntity(Base):
    """用户-企业授权（账套模式多对多）— 新表必须带entity_id（开发规范）"""
    __tablename__ = "user_entities"
    id = Column(Integer, primary_key=True, index=True)
    user_id = Column(Integer, ForeignKey("users.id"), nullable=False, index=True, comment="用户ID")
    entity_id = Column(Integer, ForeignKey("entities.id"), nullable=False, index=True, comment="企业ID（账套）")
    granted_by = Column(Integer, nullable=True, comment="授权人")
    created_at = Column(DateTime, server_default=func.now())
    __table_args__ = (UniqueConstraint("user_id", "entity_id", name="uq_user_entity"),)


class StrategicMap(Base):
    """战略地图"""
    __tablename__ = "strategic_maps"
    id = Column(Integer, primary_key=True, index=True)
    entity_id = Column(Integer, default=1, comment="企业ID (多租户隔离 2026-08-25)")
    title = Column(String(200), nullable=False, comment="地图名称")
    version = Column(String(20), default="v1.0", comment="版本号")
    status = Column(String(20), default="draft", comment="draft/published")
    dimensions = Column(JSON, nullable=True, comment="四维度和目标列表")
    canvas_data = Column(JSON, nullable=True, comment="画布连线数据")
    created_by = Column(Integer, nullable=True)
    created_at = Column(DateTime, server_default=func.now())
    updated_at = Column(DateTime, server_default=func.now(), onupdate=func.now())


class KPIDefinition(Base):
    """KPI字典"""
    __tablename__ = "kpi_definitions"
    id = Column(Integer, primary_key=True, index=True)
    entity_id = Column(Integer, default=1, comment="企业ID")
    map_id = Column(Integer, ForeignKey("strategic_maps.id"), nullable=True, comment="关联战略地图")
    kpi_code = Column(String(50), unique=True, nullable=False, comment="KPI编码")
    kpi_name = Column(String(200), nullable=False, comment="KPI名称")
    dimension = Column(String(50), comment="所属维度: finance/customer/process/learning")
    objective = Column(String(200), comment="关联战略目标")
    formula = Column(Text, nullable=True, comment="计算公式")
    formula_desc = Column(String(500), nullable=True, comment="公式说明")
    data_source_type = Column(String(20), default="manual", comment="erp/business/excel/manual")
    data_source_config = Column(JSON, nullable=True, comment="数据源配置")
    data_source = Column(String(500), default="待补充", comment="数据来源")
    data_owner = Column(String(100), default="待指定", comment="数据责任人")
    frequency = Column(String(20), default="monthly", comment="daily/weekly/monthly/quarterly/yearly")
    unit = Column(String(50), default="%", comment="单位")
    target_value = Column(Float, nullable=True, comment="目标值(兼容旧字段)")
    target_monthly = Column(Float, nullable=True, comment="月度目标值")
    target_quarterly = Column(Float, nullable=True, comment="季度目标值")
    target_yearly = Column(Float, nullable=True, comment="年度目标值")
    target_calc_type = Column(String(20), nullable=True, comment="指标类型: accumulate累计(月×3=季,×12=年) / ratio比率(季/年沿用基准,可手调)")
    threshold_green = Column(String(100), nullable=True, comment="绿灯阈值")
    threshold_yellow = Column(String(100), nullable=True, comment="黄灯阈值")
    threshold_red = Column(String(100), nullable=True, comment="红灯阈值")
    category = Column(String(50), nullable=True, comment="BSC二级类别: revenue_growth/profitability/cost_control/asset_efficiency/cash_risk/customer_scale/customer_concentration/customer_satisfaction/supply_chain/delivery_quality/talent_pipeline/employee_engagement/innovation")
    important_flag = Column(Integer, default=0, comment="是否重要数据(1=是, 数据分类分级 2026-08-26)")
    data_level = Column(String(20), nullable=True, comment="数据分级: core核心/important重要/general一般")
    data_category = Column(String(50), nullable=True, comment="行业参考分类(自定义,如金融/医疗/工业)")
    responsible_dept = Column(String(200), nullable=True, comment="负责部门")
    responsible_user = Column(String(100), nullable=True, comment="负责人")
    kpi_level = Column(String(20), default="operational", comment="strategic/operational")
    status = Column(String(20), default="active")
    bot_source = Column(String(50), nullable=True, comment="Bot标识: finance-bot/ops-bot等")
    epic = Column(String(50), default="Epic2", comment="所属Epic")
    created_by = Column(Integer, nullable=True)
    created_at = Column(DateTime, server_default=func.now())
    updated_at = Column(DateTime, server_default=func.now(), onupdate=func.now())


class KPIValue(Base):
    """KPI实际值"""
    __tablename__ = "kpi_values"
    id = Column(Integer, primary_key=True, index=True)
    entity_id = Column(Integer, default=None, comment="企业ID (P2多租户隔离 2026-08-23, 按kpi_id回填)")
    kpi_id = Column(Integer, ForeignKey("kpi_definitions.id"), nullable=False)
    period = Column(String(20), nullable=False, comment="期间 2026-05")
    actual_value = Column(Float, nullable=True, comment="实际值")
    source_type = Column(String(20), default="manual", comment="erp/excel/manual")
    source_batch = Column(String(100), nullable=True, comment="导入批次号")
    data_status = Column(String(20), default="pending", comment="pending/verified/error")
    calculated_at = Column(DateTime, server_default=func.now())
    remark = Column(String(500), nullable=True)


class DataSourceConfig(Base):
    """数据源配置"""
    __tablename__ = "data_source_config"
    id = Column(Integer, primary_key=True, index=True)
    name = Column(String(200), nullable=False, comment="数据源名称")
    source_type = Column(String(20), nullable=False, comment="erp/business/excel")
    api_endpoint = Column(String(500), nullable=True, comment="API地址")
    api_key = Column(String(200), nullable=True, comment="API Key")
    query_sql = Column(Text, nullable=True, comment="SQL查询语句")
    sync_type = Column(String(20), default="realtime", comment="realtime/batch")
    status = Column(String(20), default="active")
    last_sync_at = Column(DateTime, nullable=True)
    created_at = Column(DateTime, server_default=func.now())


class KPIAlert(Base):
    """预警记录"""
    __tablename__ = "kpi_alerts"
    id = Column(Integer, primary_key=True, index=True)
    kpi_id = Column(Integer, ForeignKey("kpi_definitions.id"), nullable=False)
    kpi_value_id = Column(Integer, ForeignKey("kpi_values.id"), nullable=True)
    alert_level = Column(String(20), default="yellow", comment="green/yellow/red")
    alert_message = Column(String(500), nullable=False)
    alert_type = Column(String(30), default="actual", comment="actual/forecast — 实际值超限/预测值超限")
    suggestion = Column(Text, nullable=True, comment="情景建议")
    action_plan_linked_id = Column(Integer, nullable=True, comment="关联的改善计划ID")
    status = Column(String(20), default="pending", comment="pending/processing/resolved")
    assignee = Column(String(100), nullable=True, comment="处理人")
    resolution = Column(Text, nullable=True, comment="处理结果")
    resolved_at = Column(DateTime, nullable=True)
    created_at = Column(DateTime, server_default=func.now())


class OperationLog(Base):
    """操作日志"""
    __tablename__ = "operation_logs"
    id = Column(Integer, primary_key=True, index=True)
    user_id = Column(Integer, nullable=True)
    action = Column(String(50), nullable=False, comment="create/update/delete/calculate/import")
    target_type = Column(String(50), nullable=False, comment="kpi/map/alert/source")
    target_id = Column(Integer, nullable=True)
    detail = Column(JSON, nullable=True)
    created_at = Column(DateTime, server_default=func.now())


class NotificationChannel(Base):
    """通知渠道配置"""
    __tablename__ = "notification_channels"
    id = Column(Integer, primary_key=True, index=True)
    name = Column(String(100), nullable=False, comment="渠道名称")
    channel_type = Column(String(30), nullable=False, comment="wecom/mail/sms")
    config = Column(JSON, nullable=True, comment="渠道配置")
    enabled = Column(Boolean, default=True)
    created_at = Column(DateTime, server_default=func.now())


class NotificationLog(Base):
    """通知发送日志"""
    __tablename__ = "notification_logs"
    id = Column(Integer, primary_key=True, index=True)
    alert_id = Column(Integer, ForeignKey("kpi_alerts.id"), nullable=True)
    channel = Column(String(30), nullable=False, comment="wecom/mail")
    recipient = Column(String(200), nullable=True, comment="收件人")
    title = Column(String(200), nullable=True)
    content = Column(Text, nullable=True)
    status = Column(String(20), default="pending", comment="pending/sent/failed")
    error_msg = Column(String(500), nullable=True)
    sent_at = Column(DateTime, nullable=True)
    created_at = Column(DateTime, server_default=func.now())


class RolePermission(Base):
    """角色权限配置（单条记录，key-value）"""
    __tablename__ = "role_permissions"
    id = Column(Integer, primary_key=True, index=True)
    key = Column(String(50), unique=True, nullable=False, comment="配置键: route_permissions / action_permissions")
    value = Column(JSON, nullable=False, comment="配置值")
    updated_at = Column(DateTime, server_default=func.now(), onupdate=func.now())


class Objective(Base):
    """OKR目标"""
    __tablename__ = "objectives"
    id = Column(Integer, primary_key=True, index=True)
    entity_id = Column(Integer, default=1, comment="企业ID (P2多租户隔离 2026-08-23)")
    title = Column(String(200), nullable=False, comment="目标标题")
    description = Column(Text, nullable=True, comment="目标描述")
    dimension = Column(String(50), nullable=True, comment="关联维度: finance/customer/process/learning")
    strategic_map_id = Column(Integer, ForeignKey("strategic_maps.id"), nullable=True, comment="关联战略地图")
    quarter = Column(String(20), nullable=False, comment="季度: 2026Q3")
    owner = Column(String(100), nullable=True, comment="负责人")
    status = Column(String(20), default="active", comment="active/completed/cancelled")
    progress = Column(Integer, default=0, comment="整体进度 0-100")
    confidence = Column(Integer, default=5, comment="信心指数 1-10")
    created_at = Column(DateTime, server_default=func.now())
    updated_at = Column(DateTime, server_default=func.now(), onupdate=func.now())


class ActionPlan(Base):
    """改善行动计划"""
    __tablename__ = "action_plans"
    id = Column(Integer, primary_key=True, index=True)
    alert_id = Column(Integer, ForeignKey("kpi_alerts.id"), nullable=True, comment="关联预警")
    kpi_id = Column(Integer, ForeignKey("kpi_definitions.id"), nullable=False, comment="关联KPI")
    objective_id = Column(Integer, ForeignKey("objectives.id"), nullable=True, comment="关联OKR目标")
    kr_id = Column(Integer, ForeignKey("krs.id"), nullable=True, comment="关联KR (行动挂KR 2026-08-27)")
    title = Column(String(200), nullable=False, comment="计划标题")
    description = Column(Text, nullable=True, comment="详细描述")
    assignee = Column(String(100), nullable=True, comment="负责人")
    priority = Column(String(20), default="medium", comment="high/medium/low")
    due_date = Column(DateTime, nullable=True, comment="截止日期")
    status = Column(String(20), default="pending", comment="pending/in_progress/completed/cancelled")
    progress = Column(Integer, default=0, comment="完成进度 0-100")
    result = Column(Text, nullable=True, comment="改善结果")
    monthly_milestones = Column(JSON, nullable=True, comment="月度里程碑: [{\"month\":\"2026-07\",\"label\":\"...\",\"status\":\"completed\"}]")
    auto_verify_rule = Column(JSON, nullable=True, comment="自动验证规则: {\"condition\": \"value > target\"}")
    verify_result = Column(String(20), nullable=True, comment="验证结果: pass/fail/pending")
    verify_log = Column(JSON, nullable=True, comment="验证历史日志")
    verify_status = Column(String(20), default="pending", comment="验证状态: pending/passed/failed/retrying/escalated")
    verify_attempts = Column(Integer, default=0, comment="验证尝试次数")
    verified_at = Column(DateTime, nullable=True, comment="验证完成时间")
    kpi_current_before = Column(Float, nullable=True, comment="执行前KPI值")
    kpi_current_after = Column(Float, nullable=True, comment="执行后KPI值")
    created_by = Column(String(100), nullable=True, comment="创建人")
    created_at = Column(DateTime, server_default=func.now())
    updated_at = Column(DateTime, server_default=func.now(), onupdate=func.now())


class OrgNode(Base):
    """组织节点: 集团→事业部→区域→部门→班组 5级"""
    __tablename__ = "org_nodes"
    id = Column(Integer, primary_key=True, index=True)
    parent_id = Column(Integer, ForeignKey("org_nodes.id"), nullable=True, comment="父节点ID")
    name = Column(String(100), nullable=False, comment="节点名称")
    code = Column(String(50), unique=True, nullable=True, comment="编码")
    level = Column(Integer, nullable=False, comment="1=集团 2=事业部 3=区域 4=部门 5=班组")
    sort_order = Column(Integer, default=0, comment="排序")
    enabled = Column(Integer, default=1, comment="1启用 0禁用")
    path = Column(String(500), nullable=True, comment="路径")
    remark = Column(String(200), nullable=True, comment="备注")
    created_at = Column(DateTime, server_default=func.now())
    updated_at = Column(DateTime, server_default=func.now(), onupdate=func.now())


class StrategicMapVersion(Base):
    """战略地图版本快照"""
    __tablename__ = "strategic_map_versions"
    id = Column(Integer, primary_key=True, index=True)
    entity_id = Column(Integer, default=1, comment="企业ID (OpenCode审查#9 2026-08-26)")
    map_id = Column(Integer, ForeignKey("strategic_maps.id", ondelete="CASCADE"), nullable=False, comment="关联地图")
    version = Column(String(20), nullable=False, comment="版本号 v1.0 v1.1 ...")
    dimensions = Column(JSON, nullable=False, comment="维度数据快照")
    canvas_data = Column(JSON, nullable=False, comment="画布数据快照")
    comment = Column(String(500), nullable=True, comment="说明")
    created_by = Column(Integer, nullable=True)
    created_at = Column(DateTime, server_default=func.now())


class ReviewRecord(Base):
    """复盘记录 — 战略回顾会/运营复盘会结论持久化（大PDCA Act闭环 2026-08-27）"""
    __tablename__ = "review_records"
    id = Column(Integer, primary_key=True, index=True)
    entity_id = Column(Integer, default=1, comment="企业ID (多租户隔离 2026-08-27)")
    map_id = Column(Integer, ForeignKey("strategic_maps.id", ondelete="CASCADE"), nullable=False, comment="关联战略地图")
    review_type = Column(String(20), default="quarterly", comment="monthly/quarterly 月度运营复盘/季度战略回顾")
    review_date = Column(Date, nullable=False, comment="复盘日期")
    summary = Column(Text, nullable=False, comment="复盘结论（为什么没达成/怎么调整）")
    adjustments = Column(JSON, nullable=True, comment="调整项JSON [{type,target,action}]")
    next_priorities = Column(JSON, nullable=True, comment="下阶段优先级 [str,...]")
    created_by = Column(Integer, nullable=True, comment="创建人用户ID")
    created_at = Column(DateTime, server_default=func.now())
    updated_at = Column(DateTime, server_default=func.now(), onupdate=func.now())


class MapObjective(Base):
    """战略地图目标: 每个维度下的具体目标"""
    __tablename__ = "map_objectives"
    id = Column(Integer, primary_key=True, index=True)
    entity_id = Column(Integer, default=1, comment="企业ID (多租户隔离 2026-08-25)")
    map_id = Column(Integer, ForeignKey("strategic_maps.id", ondelete="CASCADE"), nullable=False, comment="关联地图")
    dimension_key = Column(String(50), nullable=False, comment="所属维度: finance/customer/process/learning")
    name = Column(String(200), nullable=False, comment="目标名称")
    description = Column(Text, nullable=True, comment="描述")
    icon = Column(String(50), default="target", comment="图标标识")
    kpis = Column(JSON, nullable=True, comment="关联KPI编码数组")
    sort_order = Column(Integer, default=0, comment="排序")
    created_at = Column(DateTime, server_default=func.now())
    updated_at = Column(DateTime, server_default=func.now(), onupdate=func.now())

class KPICausality(Base):
    """KPI因果链 — 记录KPI间的因果关系"""
    __tablename__ = "kpi_causality"
    id = Column(Integer, primary_key=True, index=True)
    entity_id = Column(Integer, default=1, comment="企业ID (多租户隔离 2026-08-27)")
    source_kpi_id = Column(Integer, ForeignKey("kpi_definitions.id"), nullable=False, comment="源KPI（因）")
    target_kpi_id = Column(Integer, ForeignKey("kpi_definitions.id"), nullable=False, comment="目标KPI（果）")
    strength = Column(Float, default=0.5, comment="影响强度 0~1")
    lag_months = Column(Integer, default=1, comment="滞后期（月）")
    formula = Column(String(500), nullable=True, comment="影响公式描述")
    direction = Column(String(10), default="positive", comment="positive/negative 正向/负向影响")
    source_type = Column(String(20), default="manual", comment="建链来源 AI_suggested/manual/imported (2026-08-27 验证机制)")
    verify_status = Column(String(20), default="pending", comment="验证状态 pending/data_verified/human_verified/disputed (2026-08-27)")
    verified_at = Column(DateTime, nullable=True, comment="验证时间")
    verified_by = Column(String(50), nullable=True, comment="验证人/AI/脚本")
    created_at = Column(DateTime, server_default=func.now())
    updated_at = Column(DateTime, server_default=func.now(), onupdate=func.now())


class MpmResult(Base):
    """MPM财务Bot分析结果记录"""
    __tablename__ = "mpm_results"
    id = Column(Integer, primary_key=True, index=True)
    entity_id = Column(Integer, ForeignKey("entities.id"), nullable=False, comment="企业实体ID")
    period = Column(String(20), nullable=False, comment="期间 YYYY-MM")
    source = Column(String(50), default="finance-bot", comment="来源Bot标识")
    raw_data = Column(JSON, nullable=False, comment="完整的MPM计算结果")
    created_at = Column(DateTime, server_default=func.now())


class KpiForecastLog(Base):
    """KPI预测历史 — 预测偏差告警数据源 (2026-08-25 升级2a)"""
    __tablename__ = "kpi_forecast_log"
    id = Column(Integer, primary_key=True, index=True)
    entity_id = Column(Integer, nullable=False, comment="企业ID")
    kpi_id = Column(Integer, nullable=False, comment="KPI ID")
    kpi_code = Column(String(50), nullable=False, comment="KPI编码")
    period = Column(String(20), nullable=False, comment="预测期间")
    forecast_value = Column(Float, nullable=True, comment="预测值")
    model = Column(String(30), default="linear", comment="预测模型")
    confidence = Column(String(10), nullable=True, comment="置信度")
    trend = Column(String(10), nullable=True, comment="趋势")
    created_at = Column(DateTime, server_default=func.now())


class BotBridgeConfig(Base):
    """Bot桥接鉴权配置"""
    __tablename__ = "bot_bridge_config"
    id = Column(Integer, primary_key=True, index=True)
    bot_name = Column(String(50), unique=True, nullable=False, comment="Bot名称")
    token = Column(String(64), nullable=False, comment="鉴权Token")
    is_active = Column(Boolean, default=True, comment="是否激活")
    created_at = Column(DateTime, server_default=func.now())


class KpiDataQualityLog(Base):
    """数据质量监控日志"""
    __tablename__ = "kpi_data_quality_log"
    id = Column(Integer, primary_key=True, index=True)
    kpi_id = Column(Integer, ForeignKey("kpi_definitions.id"), nullable=False, comment="关联KPI")
    check_type = Column(String(30), nullable=False, comment="abnormal_change/flat_data/missing_data/value_outlier")
    severity = Column(String(20), default="warning", comment="info/warning/critical")
    detail = Column(JSON, nullable=True, comment="检测详情")
    suggestion = Column(String(500), nullable=True, comment="建议操作")
    status = Column(String(20), default="open", comment="open/resolved/ignored")
    resolved_at = Column(DateTime, nullable=True)
    created_at = Column(DateTime, server_default=func.now())


class BiReportTemplate(Base):
    """BI报表模板"""
    __tablename__ = "bi_report_templates"
    id = Column(Integer, primary_key=True, index=True)
    entity_id = Column(Integer, default=1, comment="企业ID (P2多租户隔离 2026-08-23)")
    name = Column(String(200), nullable=False, comment="模板名称")
    report_type = Column(String(50), nullable=False, comment="overview/trend/comparison/topn/causality")
    config = Column(JSON, nullable=False, comment="报表配置")
    is_system = Column(Integer, default=0, comment="系统预置模板")
    created_by = Column(Integer, nullable=True)
    created_at = Column(DateTime, server_default=func.now())
    updated_at = Column(DateTime, server_default=func.now(), onupdate=func.now())


class BiReport(Base):
    """用户保存的BI报表"""
    __tablename__ = "bi_reports"
    id = Column(Integer, primary_key=True, index=True)
    entity_id = Column(Integer, default=1, comment="企业ID (P2多租户隔离 2026-08-23)")
    template_id = Column(Integer, ForeignKey("bi_report_templates.id"), nullable=True)
    name = Column(String(200), nullable=False, comment="报表名称")
    config = Column(JSON, nullable=False, comment="报表配置（行/列/值）")
    chart_type = Column(String(50), default="auto", comment="图表类型")
    is_shared = Column(Integer, default=0, comment="是否分享")
    created_by = Column(Integer, nullable=True)
    created_at = Column(DateTime, server_default=func.now())
    updated_at = Column(DateTime, server_default=func.now(), onupdate=func.now())


class BscLayerConfig(Base):
    """BSC四层配置 — 不同企业的权重配置"""
    __tablename__ = "bsc_layer_config"
    id = Column(Integer, primary_key=True, index=True)
    entity_id = Column(Integer, ForeignKey("entities.id"), nullable=False, comment="企业ID")
    layer = Column(String(20), nullable=False, comment="financial/customer/process/learning")
    weight = Column(Float, nullable=False, comment="该层权重（%）")
    kpi_count_min = Column(Integer, default=2, comment="最少KPI数")
    kpi_count_max = Column(Integer, default=5, comment="最多KPI数")


class KPIHierarchy(Base):
    """KPI层级关系 — 公司→部门→个人三级分解"""
    __tablename__ = "kpi_hierarchy"
    id = Column(Integer, primary_key=True, index=True)
    entity_id = Column(Integer, ForeignKey("entities.id"), default=1, comment="企业ID")
    parent_kpi_id = Column(Integer, ForeignKey("kpi_definitions.id"), nullable=False, comment="上级KPI")
    child_kpi_id = Column(Integer, ForeignKey("kpi_definitions.id"), nullable=False, comment="下级KPI")
    level = Column(Integer, default=1, comment="1=公司级 2=部门级 3=个人级")
    weight = Column(Float, default=1.0, comment="下级对上级的贡献权重（%）")
    child_name = Column(String(200), nullable=True, comment="下级节点名称（个人或部门名）")
    created_at = Column(DateTime, server_default=func.now())


# 兼容性: P2开发新增的模板API需要的模型
# KPI模板（独立表）
class KPITemplate(Base):
    __tablename__ = "kpi_templates"
    id = Column(Integer, primary_key=True, index=True)
    kpi_code = Column(String(50), unique=True, nullable=False)
    kpi_name = Column(String(200), nullable=False)
    dimension = Column(String(50))
    category = Column(String(50))
    formula = Column(Text)
    formula_desc = Column(String(500))
    unit = Column(String(50))
    target_value = Column(Float)
    description = Column(Text)
    is_system = Column(Integer, default=0)
    usage_count = Column(Integer, default=0)
    created_by = Column(Integer)
    created_at = Column(DateTime, server_default=func.now())
    updated_at = Column(DateTime, server_default=func.now(), onupdate=func.now())


class OKRTemplate(Base):
    """OKR模板库"""
    __tablename__ = "okr_templates"
    id = Column(Integer, primary_key=True, index=True)
    name = Column(String(100), nullable=False, comment="O名称")
    description = Column(Text, comment="O描述")
    dimension = Column(String(20), nullable=False, comment="finance/customer/process/learning")
    layer = Column(String(20), default="level1", comment="level1/level2/level3")
    industry_tag = Column(String(50), default="general", comment="行业标签")
    preset_krs = Column(JSON, nullable=False, comment="预设关键结果列表")
    source = Column(String(20), default="system", comment="system/user/industry_pack")
    owner = Column(String(50), default="CMA标准库", comment="模板负责人（治理P2: 可追踪）")
    use_count = Column(Integer, default=0, comment="使用次数")
    sort_order = Column(Integer, default=0)
    is_active = Column(Integer, default=1)
    created_at = Column(DateTime, server_default=func.now())
    updated_at = Column(DateTime, server_default=func.now(), onupdate=func.now())


class CashForecast(Base):
    """现金流预测 — 每日未来30天预测"""
    __tablename__ = "cash_forecasts"
    id = Column(Integer, primary_key=True, index=True)
    entity_id = Column(Integer, ForeignKey("entities.id"), nullable=False, comment="企业ID")
    forecast_date = Column(DateTime, nullable=False, comment="预测日期（每天一条）")
    predicted_cash = Column(Float, nullable=True, comment="预测现金余额")
    lower_bound = Column(Float, nullable=True, comment="置信区间下界")
    upper_bound = Column(Float, nullable=True, comment="置信区间上界")
    alert_status = Column(String(20), default="green", comment="green/yellow/red")
    created_at = Column(DateTime, server_default=func.now())


class CashPlan(Base):
    """收付款计划 — 资金管理智能体（唯一应收载体：含回款登记、负责人、数据来源）"""
    __tablename__ = "cash_plans"
    id = Column(Integer, primary_key=True, index=True)
    entity_id = Column(Integer, ForeignKey("entities.id"), default=1, comment="企业ID")
    plan_type = Column(String(10), nullable=False, comment="receive收/pay付")
    related_kpi_id = Column(Integer, nullable=True, comment="关联KPI(预算联动 2026-08-27)")
    budget_plan_id = Column(Integer, nullable=True, comment="来源预算计划ID")
    amount = Column(Float, nullable=False, comment="金额(万元)")
    plan_date = Column(DateTime, nullable=False, comment="计划日期(应收即到期日)")
    counterparty = Column(String(200), nullable=True, comment="关联客户/供应商")
    description = Column(String(500), nullable=True, comment="说明")
    status = Column(String(20), default="pending", comment="pending/completed/cancelled")
    owner = Column(String(100), nullable=True, comment="负责人/业务员(应收催收责任人)")
    source = Column(String(50), default="manual", comment="数据来源: manual/bohai_ar/receivables_migrate")
    paid_amount = Column(Float, default=0, comment="已回款金额(万元)")
    completed_at = Column(DateTime, nullable=True, comment="完成时间")
    created_at = Column(DateTime, server_default=func.now())
    updated_at = Column(DateTime, server_default=func.now(), onupdate=func.now())


class SystemConfig(Base):
    """系统配置 — key-value存储"""
    __tablename__ = "system_configs"
    id = Column(Integer, primary_key=True, index=True)
    config_key = Column(String(100), unique=True, nullable=False, comment="配置键")
    config_value = Column(String(500), nullable=True, comment="配置值")
    description = Column(String(500), nullable=True, comment="配置说明")
    updated_at = Column(DateTime, server_default=func.now(), onupdate=func.now())


class BudgetDeviationAlert(Base):
    """预算偏差预警记录"""
    __tablename__ = "budget_deviation_alerts"
    id = Column(Integer, primary_key=True, index=True)
    entity_id = Column(Integer, default=1, comment="企业ID (P2多租户隔离 2026-08-23)")
    kpi_id = Column(Integer, ForeignKey("kpi_definitions.id"), nullable=False, comment="关联KPI")
    period = Column(String(20), nullable=False, comment="期间 YYYY-MM")
    budget_value = Column(Float, nullable=True, comment="预算值")
    actual_value = Column(Float, nullable=True, comment="实际值")
    deviation_rate = Column(Float, nullable=True, comment="偏差率 %")
    deviation_value = Column(Float, nullable=True, comment="偏差绝对值")
    alert_level = Column(String(20), default="warning", comment="warning/critical")
    status = Column(String(20), default="open", comment="open/resolved/ignored")
    suggestion = Column(String(500), nullable=True, comment="处理建议")
    alert_type = Column(String(30), nullable=True, comment="归因场景: cost_high/revenue_drop/cash_low/cash_critical (2026-08-28 告警归因P1-③)")
    attribution = Column(JSON, nullable=True, comment="归因JSON: 子KPI拆解+科目拆解+量价差+趋势 (2026-08-28)")
    scenario_id = Column(Integer, nullable=True, comment="FK scenario_suggestions.id 场景建议 (2026-08-28)")
    created_at = Column(DateTime, server_default=func.now())


class ReportHistory(Base):
    """自动生成的经营分析报告记录"""
    __tablename__ = "report_history"
    id = Column(Integer, primary_key=True, index=True)
    report_type = Column(String(20), nullable=False, comment="weekly/monthly/special")
    period = Column(String(20), nullable=False, comment="期间: 2026-W30 / 2026-07 / 2026-Q2")
    title = Column(String(200), nullable=False, comment="报告标题")
    markdown_content = Column(Text, nullable=True, comment="Markdown格式报告（用于微信推送）")
    json_content = Column(JSON, nullable=True, comment="JSON结构化数据（写入CMA系统）")
    status = Column(String(20), default="generated", comment="generated/pushed/failed")
    trigger_type = Column(String(20), default="manual", comment="manual/scheduled/event")
    alert_ref = Column(String(50), nullable=True, comment="事件触发时的预警引用")
    created_at = Column(DateTime, server_default=func.now())


class AnalysisResult(Base):
    """财务Bot分析结论 — 带置信度评分"""
    __tablename__ = "analysis_results"
    id = Column(Integer, primary_key=True, index=True)
    period = Column(String(20), nullable=False, comment="期间 YYYY-MM")
    conclusion = Column(String(1000), nullable=False, comment="分析结论")
    confidence = Column(Integer, nullable=False, comment="置信度 0-100")
    data_source = Column(String(500), nullable=True, comment="数据来源")
    calculation_logic = Column(String(1000), nullable=True, comment="计算逻辑")
    comparable_benchmark = Column(String(500), nullable=True, comment="可比基准")
    limitations = Column(String(1000), nullable=True, comment="局限说明")
    has_actual = Column(Integer, default=0, comment="有实际值")
    has_target = Column(Integer, default=0, comment="有目标值")
    has_trend = Column(Integer, default=0, comment="有历史趋势")
    has_review = Column(Integer, default=0, comment="有人工复核")
    kpi_code = Column(String(50), nullable=True, comment="关联KPI编码")
    kpi_name = Column(String(200), nullable=True, comment="关联KPI名称")
    created_at = Column(DateTime, server_default=func.now())


class ForecastAccuracy(Base):
    """预测准确率 — 上期预测 vs 本期实际"""
    __tablename__ = "forecast_accuracy"
    id = Column(Integer, primary_key=True, index=True)
    entity_id = Column(Integer, ForeignKey("entities.id"), nullable=False, comment="企业ID")
    period = Column(String(20), nullable=False, comment="期间 2026-07")
    forecast_value = Column(Float, nullable=True, comment="预测值")
    actual_value = Column(Float, nullable=True, comment="实际值")
    mae = Column(Float, nullable=True, comment="绝对误差")
    mape = Column(Float, nullable=True, comment="百分比误差")
    created_at = Column(DateTime, server_default=func.now())


class ScenarioSuggestion(Base):
    """情景建议模板 — 根据不同预警类型自动生成建议"""
    __tablename__ = "scenario_suggestions"
    id = Column(Integer, primary_key=True, index=True)
    alert_type = Column(String(30), nullable=False, comment="预警类型: cash_low/cash_critical/cost_high/revenue_drop")
    title = Column(String(200), nullable=False, comment="建议标题")
    description = Column(Text, nullable=True, comment="详细建议")
    action_template = Column(Text, nullable=True, comment="改善行动模板")
    priority = Column(String(20), default="medium", comment="high/medium/low")
    sort_order = Column(Integer, default=0, comment="排序")
    created_at = Column(DateTime, server_default=func.now())


# ============================================================
# 新30号准则模型 (2027)
# ============================================================

class Subject(Base):
    """会计科目 — 新30号准则分类"""
    __tablename__ = "subjects"
    id = Column(Integer, primary_key=True, index=True)
    subject_code = Column(String(20), nullable=False, unique=True, comment="科目编码")
    subject_name = Column(String(200), nullable=False, comment="科目名称")
    parent_code = Column(String(20), nullable=True, comment="上级科目编码")
    level = Column(Integer, default=1, comment="科目级别 1-4")
    category = Column(String(50), nullable=True, comment="科目类别")
    new_standard_category = Column(String(20), nullable=True, comment="新30号准则分类: operating/investing/financing/tax/discontinued")
    important_flag = Column(Integer, default=0, comment="是否重要数据(1=是, 数据分类分级 2026-08-26)")
    data_level = Column(String(20), nullable=True, comment="数据分级: core核心/important重要/general一般")
    data_category = Column(String(50), nullable=True, comment="行业参考分类(自定义,如金融/医疗/工业)")
    is_active = Column(Integer, default=1, comment="是否启用")
    remark = Column(String(500), nullable=True, comment="备注")
    created_at = Column(DateTime, server_default=func.now())
    updated_at = Column(DateTime, server_default=func.now(), onupdate=func.now())


class VoucherDetail(Base):
    """凭证明细 — 新30号准则分类 (网银流水导入 2026-08-28)"""
    __tablename__ = "voucher_details"
    id = Column(Integer, primary_key=True, index=True)
    entity_id = Column(Integer, default=1, nullable=False, comment="企业ID (多租户隔离)")
    voucher_no = Column(String(50), nullable=False, comment="凭证编号")
    voucher_date = Column(DateTime, nullable=False, comment="凭证日期")
    subject_code = Column(String(20), nullable=False, comment="科目编码")
    subject_name = Column(String(200), nullable=True, comment="科目名称")
    debit_amount = Column(Float, default=0, comment="借方金额")
    credit_amount = Column(Float, default=0, comment="贷方金额")
    summary = Column(String(500), nullable=True, comment="摘要")
    carry_forward = Column(Integer, default=0, nullable=False, comment="结转行标记(1=结转行不参与现金流)")
    new_standard_category = Column(String(20), nullable=True, comment="新30号准则分类")
    period = Column(String(20), nullable=True, comment="期间 YYYY-MM")
    batch = Column(String(100), nullable=True, comment="导入批次号")
    created_at = Column(DateTime, server_default=func.now())


class ImportLog(Base):
    """数据导入日志 — 网银流水/Excel导入批次记录 (2026-08-28)"""
    __tablename__ = "import_logs"
    id = Column(Integer, primary_key=True, index=True)
    entity_id = Column(Integer, default=1, nullable=False, comment="企业ID (多租户隔离)")
    filename = Column(String(500), nullable=False, comment="文件名")
    batch = Column(String(100), nullable=False, comment="批次号")
    total_rows = Column(Integer, nullable=True, comment="总行数")
    success_rows = Column(Integer, nullable=True, comment="成功行数")
    failed_rows = Column(Integer, nullable=True, comment="失败行数")
    errors = Column(JSON, nullable=True, comment="失败详情 [{row, field, reason}]")
    period = Column(String(20), nullable=True, comment="导入期间")
    import_type = Column(String(20), nullable=True, comment="导入类型: vouchers/kpi")
    created_by = Column(String(100), nullable=True, comment="导入人")
    created_at = Column(DateTime, server_default=func.now())


# ============================================================
# 费用审核智能体 (2026-08)
# ============================================================

class ExpenseRule(Base):
    """费用规则 — 自动校验报销单的标准"""
    __tablename__ = "expense_rules"
    id = Column(Integer, primary_key=True, index=True)
    entity_id = Column(Integer, default=1, comment="企业ID (P2多租户隔离 2026-08-23)")
    rule_name = Column(String(100), nullable=False, comment="规则名称")
    dimension = Column(String(20), nullable=False, comment="维度: department/person/expense_type")
    dimension_value = Column(String(100), nullable=True, comment="维度值: 部门名/人员名/费用类型(空=全局)")
    expense_type = Column(String(30), nullable=False, comment="费用类型: entertainment/travel/office/management")
    limit_type = Column(String(20), nullable=False, comment="限额类型: single/monthly/yearly 单笔/月度累计/年度累计")
    limit_amount = Column(Float, nullable=False, comment="限额金额")
    cycle = Column(String(20), default="monthly", comment="周期: single/monthly/yearly")
    status = Column(String(20), default="active", comment="active/inactive")
    remark = Column(String(500), nullable=True, comment="备注")
    created_at = Column(DateTime, server_default=func.now())
    updated_at = Column(DateTime, server_default=func.now(), onupdate=func.now())


class ExpenseReimbursement(Base):
    """费用报销单 — 提交后自动校验规则，超限自动打回"""
    __tablename__ = "expense_reimbursements"
    id = Column(Integer, primary_key=True, index=True)
    entity_id = Column(Integer, default=1, comment="企业ID (P2多租户隔离 2026-08-23)")
    reimb_no = Column(String(50), unique=True, nullable=False, comment="报销单号")
    applicant = Column(String(100), nullable=False, comment="申请人")
    department = Column(String(100), nullable=True, comment="部门")
    expense_type = Column(String(30), nullable=False, comment="费用类型: entertainment/travel/office/management")
    title = Column(String(200), nullable=False, comment="事由/摘要")
    amount = Column(Float, nullable=False, comment="报销金额")
    expense_date = Column(DateTime, nullable=True, comment="费用发生日期")
    attachment = Column(String(500), nullable=True, comment="附件文件名")
    status = Column(String(20), default="pending", comment="pending待审批/approved已通过/rejected已拒绝/returned已打回")
    check_result = Column(String(20), default="pass", comment="自动校验结果: pass/fail")
    check_reason = Column(String(1000), nullable=True, comment="超限原因")
    check_detail = Column(JSON, nullable=True, comment="校验明细: [{rule_name, limit, actual, passed}]")
    checked_at = Column(DateTime, nullable=True, comment="自动校验时间")
    approver = Column(String(100), nullable=True, comment="审批人")
    approve_comment = Column(String(500), nullable=True, comment="审批意见")
    approved_at = Column(DateTime, nullable=True, comment="审批时间")
    created_by = Column(String(100), nullable=True, comment="提交人")
    created_at = Column(DateTime, server_default=func.now())
    updated_at = Column(DateTime, server_default=func.now(), onupdate=func.now())


# ============================================================
# 税务合规智能体 (2026-08)
#   ① 税负监控  ② 发票校验  ③ 社保比对
# ============================================================

class TaxRecord(Base):
    """税务记录 — 税负监控：应纳税额/实缴额/税负率 vs 行业基准"""
    __tablename__ = "tax_records"
    id = Column(Integer, primary_key=True, index=True)
    entity_id = Column(Integer, default=1, comment="企业ID")
    period = Column(String(20), nullable=False, comment="期间 YYYY-MM")
    tax_type = Column(String(30), nullable=False, comment="税种: vat增值税/income所得税/surtax附加税")
    tax_payable = Column(Float, default=0, comment="应纳税额")
    tax_paid = Column(Float, default=0, comment="实缴税额")
    tax_rate = Column(Float, nullable=True, comment="适用税率 %")
    income = Column(Float, default=0, comment="当期收入/计税收入（税负率分母）")
    tax_burden_rate = Column(Float, nullable=True, comment="税负率 % = 实缴税额/收入×100")
    burden_status = Column(String(20), default="normal", comment="normal正常/warning超基准±20%内/alert超基准±20%")
    warning_msg = Column(String(500), nullable=True, comment="预警信息")
    remark = Column(String(500), nullable=True, comment="备注")
    created_at = Column(DateTime, server_default=func.now())
    updated_at = Column(DateTime, server_default=func.now(), onupdate=func.now())


class InvoiceCheck(Base):
    """发票校验 — 录入后按规则自动校验：号码格式/金额匹配报销单/供应商匹配合同"""
    __tablename__ = "invoice_check"
    id = Column(Integer, primary_key=True, index=True)
    entity_id = Column(Integer, default=1, comment="企业ID")
    invoice_no = Column(String(50), nullable=False, comment="发票号码")
    amount = Column(Float, nullable=False, comment="发票金额")
    invoice_type = Column(String(30), default="vat", comment="发票类型: vat专用/vat普通/electronic电子/other其他")
    invoice_date = Column(DateTime, nullable=True, comment="开票日期")
    supplier = Column(String(200), nullable=True, comment="供应商名称")
    reimb_no = Column(String(50), nullable=True, comment="关联报销单号")
    contract_no = Column(String(50), nullable=True, comment="关联合同编号")
    check_status = Column(String(20), default="pending", comment="校验状态: pending待校验/valid通过/invalid异常/warning提醒")
    check_result = Column(JSON, nullable=True, comment="校验明细: [{rule, passed, message}]")
    check_reason = Column(String(1000), nullable=True, comment="异常原因汇总")
    checked_at = Column(DateTime, nullable=True, comment="校验时间")
    created_at = Column(DateTime, server_default=func.now())
    updated_at = Column(DateTime, server_default=func.now(), onupdate=func.now())


class SocialSecurity(Base):
    """社保缴费记录 — 社保比对：基数与工资匹配/单位缴纳比例/漏缴提醒"""
    __tablename__ = "social_security"
    id = Column(Integer, primary_key=True, index=True)
    entity_id = Column(Integer, default=1, comment="企业ID")
    employee = Column(String(100), nullable=False, comment="人员姓名")
    period = Column(String(20), nullable=False, comment="期间 YYYY-MM")
    base_amount = Column(Float, default=0, comment="缴费基数")
    salary = Column(Float, nullable=True, comment="申报工资")
    company_amount = Column(Float, default=0, comment="单位缴纳金额")
    personal_amount = Column(Float, default=0, comment="个人缴纳金额")
    company_rate = Column(Float, nullable=True, comment="单位缴纳比例 % (养老16%+医疗8%+失业0.5%≈24.5%)")
    check_status = Column(String(20), default="normal", comment="normal正常/warning基数或比例异常/alert漏缴")
    warning_msg = Column(String(500), nullable=True, comment="异常提醒: 漏缴/基数不符/比例异常")
    remark = Column(String(500), nullable=True, comment="备注")
    created_at = Column(DateTime, server_default=func.now())
    updated_at = Column(DateTime, server_default=func.now(), onupdate=func.now())


# ============================================================
# 本体三支柱: 科目↔KPI↔OKR 三层互联 (2026-08-19)
# 追溯链: 目标(O) → 指标(KPI) → 科目(数据)
# ============================================================

class KPISubjectMap(Base):
    """科目↔KPI映射 — 指标计算依赖的底层会计科目"""
    __tablename__ = "kpi_subject_map"
    id = Column(Integer, primary_key=True, index=True)
    kpi_id = Column(Integer, ForeignKey("kpi_definitions.id"), nullable=False, comment="KPI ID")
    subject_code = Column(String(20), nullable=False, comment="科目编码")
    calc_type = Column(String(20), default="sum", comment="sum/avg/ratio/other")
    weight = Column(Numeric(5, 2), default=1.00, comment="权重(负=扣减项)")
    remark = Column(String(200), nullable=True, comment="备注")
    __table_args__ = (UniqueConstraint("kpi_id", "subject_code", name="uk_kpi_subject"),)


class ObjectiveKPI(Base):
    """KPI↔O支撑 — 目标由哪些KPI度量"""
    __tablename__ = "objective_kpi"
    id = Column(Integer, primary_key=True, index=True)
    entity_id = Column(Integer, default=1, comment="企业ID (P2多租户隔离 2026-08-23)")
    objective_id = Column(Integer, ForeignKey("objectives.id"), nullable=False, comment="OKR目标ID")
    kpi_id = Column(Integer, ForeignKey("kpi_definitions.id"), nullable=False, comment="KPI ID")
    weight = Column(Numeric(5, 2), default=1.00, comment="支撑权重")
    __table_args__ = (UniqueConstraint("objective_id", "kpi_id", name="uk_obj_kpi"),)


class KR(Base):
    """关键结果KR — OKR完整化 (O→KR→KPI)"""
    __tablename__ = "krs"
    id = Column(Integer, primary_key=True, index=True)
    entity_id = Column(Integer, default=1, comment="企业ID (P2多租户隔离 2026-08-23)")
    objective_id = Column(Integer, ForeignKey("objectives.id"), nullable=False, comment="OKR目标ID")
    title = Column(String(200), nullable=False, comment="KR标题")
    metric_kpi_id = Column(Integer, ForeignKey("kpi_definitions.id"), nullable=True, comment="度量KPI ID")
    operator = Column(String(5), default=">=", nullable=False, comment="方向符号: >=/<=/>/</= (KR完整修复 2026-08-27)")
    tolerance = Column(Numeric(10, 4), nullable=True, comment="容差(仅=时使用)")
    weight = Column(Numeric(5, 2), nullable=True, comment="KR权重%")
    target_value = Column(Numeric(15, 2), nullable=True, comment="目标值")
    current_value = Column(Numeric(15, 2), nullable=True, comment="当前值")
    progress = Column(Integer, default=0, comment="完成进度 0-100")
    status = Column(String(20), default="pending", comment="pending/in_progress/completed/cancelled/achieved")
    due_date = Column(Date, nullable=True, comment="截止日期")
    monthly_milestones = Column(JSON, nullable=True, comment="月度里程碑: [{\"month\":\"2026-07\",\"label\":\"...\",\"status\":\"completed\"}]")
    sort_order = Column(Integer, default=0, comment="排序")
    created_at = Column(DateTime, server_default=func.now())


# ============================================================
# 预算系统技术改进 (2026-08-28 yanxue-budget-tech-improve)
#   ① kpi_value_sources/kpi_value_collect_logs: 实际值自动归集 P1-④
#   ② budget_zero_based_items: 真零基逐项论证 P2-①
#   ③ budget_derivation_rules: 派生规则可配置 P2-②
#   ④ cash_plan_classify_rules/cash_plan_unclassified: 现金流分类规则 P2-⑥
# ============================================================

class KPIValueSource(Base):
    """KPI实际值取数映射 — 自动归集源头 (P1-④ 2026-08-28)"""
    __tablename__ = "kpi_value_sources"
    id = Column(Integer, primary_key=True, index=True)
    entity_id = Column(Integer, nullable=False, comment="企业ID(多租户隔离)")
    kpi_id = Column(Integer, ForeignKey("kpi_definitions.id"), nullable=False, comment="目标KPI")
    source_table = Column(String(50), nullable=False, comment="源头表: voucher_details/product_inventory/product_inventory_detail/cash_plans")
    source_field = Column(String(50), nullable=False, comment="金额字段: credit_amount/debit_amount/amount/qty")
    aggregate = Column(String(10), default="sum", comment="sum/avg/count/max/min")
    filter_rule = Column(JSON, nullable=True, comment="过滤: {\"subject_code\":\"6601\",\"direction\":\"credit\"}")
    period_field = Column(String(50), default="period", comment="期间字段: period/voucher_date")
    unit_conversion = Column(Float, default=1, comment="单位倍率(元→万元/10000)")
    status = Column(String(20), default="active", comment="active/inactive")
    created_at = Column(DateTime, server_default=func.now())
    updated_at = Column(DateTime, server_default=func.now(), onupdate=func.now())
    __table_args__ = (UniqueConstraint("entity_id", "kpi_id", "source_table", name="uk_source"),)


class KPIValueCollectLog(Base):
    """实际值采集日志 — 每次自动归集记录 (P1-④ 2026-08-28)"""
    __tablename__ = "kpi_value_collect_logs"
    id = Column(Integer, primary_key=True, index=True)
    entity_id = Column(Integer, nullable=False, comment="企业ID")
    kpi_id = Column(Integer, nullable=False, comment="KPI ID")
    period = Column(String(20), nullable=False, comment="期间 YYYY-MM")
    source_table = Column(String(50), nullable=False, comment="源头表")
    collected_value = Column(Float, nullable=True, comment="采集到的值")
    status = Column(String(20), default="success", comment="success/failed")
    message = Column(String(500), nullable=True, comment="说明/错误信息")
    collected_at = Column(DateTime, server_default=func.now())


class BudgetZeroBasedItem(Base):
    """零基预算逐项论证项 (P2-① 2026-08-28)"""
    __tablename__ = "budget_zero_based_items"
    id = Column(Integer, primary_key=True, index=True)
    entity_id = Column(Integer, nullable=False, comment="企业ID(多租户隔离)")
    kpi_id = Column(Integer, ForeignKey("kpi_definitions.id"), nullable=False, comment="关联KPI")
    period = Column(String(20), nullable=False, comment="期间 YYYY-MM")
    item_name = Column(String(200), nullable=False, comment="费用科目名")
    item_category = Column(String(20), default="discretionary", comment="fixed/variable/discretionary")
    base_value = Column(Float, nullable=False, comment="基准值(上年/上月实际)")
    justification = Column(Text, nullable=True, comment="逐项论证理由(为何保留/削减/取消)")
    proposed_value = Column(Float, nullable=False, comment="论证后金额")
    status = Column(String(20), default="draft", comment="draft/approved")
    created_by = Column(String(100), nullable=True, comment="创建人")
    created_at = Column(DateTime, server_default=func.now())
    updated_at = Column(DateTime, server_default=func.now(), onupdate=func.now())


class BudgetDerivationRule(Base):
    """KPI派生规则 — apply-method 可配置派生 (P2-② 2026-08-28)"""
    __tablename__ = "budget_derivation_rules"
    id = Column(Integer, primary_key=True, index=True)
    entity_id = Column(Integer, nullable=False, comment="企业ID(多租户隔离)")
    kpi_id = Column(Integer, ForeignKey("kpi_definitions.id"), nullable=False, comment="目标KPI")
    rule_type = Column(String(30), nullable=False, comment="incremental/percentage_of/formula")
    base_kpi_id = Column(Integer, nullable=True, comment="来源KPI(percentage_of用)")
    params = Column(JSON, nullable=True, comment="{\"rate\":0.02,\"field\":\"net_profit\"}")
    formula_text = Column(String(500), nullable=True, comment="可读公式说明")
    status = Column(String(20), default="active", comment="active/inactive")
    created_at = Column(DateTime, server_default=func.now())
    updated_at = Column(DateTime, server_default=func.now(), onupdate=func.now())
    __table_args__ = (UniqueConstraint("entity_id", "kpi_id", "rule_type", name="uk_rule"),)


class CashPlanClassifyRule(Base):
    """现金流收付分类规则 — KPI→receive/pay 可维护 (P2-⑥ 2026-08-28)"""
    __tablename__ = "cash_plan_classify_rules"
    id = Column(Integer, primary_key=True, index=True)
    entity_id = Column(Integer, nullable=False, comment="企业ID(多租户隔离)")
    kpi_id = Column(Integer, nullable=True, comment="精确匹配KPI,优先")
    kpi_code_pattern = Column(String(200), nullable=True, comment="关键词/编码模式匹配,兜底")
    plan_type = Column(String(10), nullable=False, comment="receive/pay")
    priority = Column(Integer, default=10, comment="匹配顺序,小优先")
    status = Column(String(20), default="active", comment="active/inactive")
    created_at = Column(DateTime, server_default=func.now())
    updated_at = Column(DateTime, server_default=func.now(), onupdate=func.now())


class CashPlanUnclassified(Base):
    """现金流待分类KPI队列 — 无法判别的KPI不静默跳过 (P2-⑥ 2026-08-28)"""
    __tablename__ = "cash_plan_unclassified"
    id = Column(Integer, primary_key=True, index=True)
    entity_id = Column(Integer, nullable=False, comment="企业ID(多租户隔离)")
    kpi_id = Column(Integer, nullable=False, comment="KPI ID")
    kpi_name = Column(String(200), nullable=True, comment="KPI名称")
    period = Column(String(20), nullable=True, comment="期间")
    budget_value = Column(Float, nullable=True, comment="预算值")
    reason = Column(String(200), nullable=True, comment="无法分类原因")
    status = Column(String(20), default="pending", comment="pending/classified/ignored")
    created_at = Column(DateTime, server_default=func.now())
    resolved_at = Column(DateTime, nullable=True)


class AISuggestion(Base):
    """AI决策建议 — 一键应用到KPI/预算/行动方案 (路线图R1 2026-08-30)"""
    __tablename__ = "ai_suggestions"
    id = Column(Integer, primary_key=True, index=True)
    entity_id = Column(Integer, default=1, comment="企业ID")
    user_id = Column(Integer, nullable=True, comment="建议创建人ID")
    source = Column(String(30), default="dashboard", comment="来源: dashboard/kpi/budget/manual/rule")
    suggestion_type = Column(String(30), nullable=False, comment="kpi_target/budget_adjust/action_plan")
    target_type = Column(String(30), nullable=False, comment="kpi/budget/action_plan")
    category = Column(String(20), default="decision", comment="分类: decision决策类 / alert预警类（预警类不推送）")
    pushed = Column(Integer, default=0, comment="决策类建议是否已推送企微 0/1（防轰炸）")
    target_id = Column(Integer, nullable=True, comment="目标ID (KPI ID/预算KPI ID等)")
    title = Column(String(300), nullable=False, comment="建议标题")
    content = Column(Text, nullable=True, comment="建议内容/理由")
    suggestion_data = Column(JSON, nullable=True, comment="应用参数: {target_value, period, budget_value, plan_title, ...}")
    status = Column(String(20), default="unapplied", comment="unapplied/applied/dismissed")
    applied_by = Column(String(100), nullable=True, comment="应用人姓名")
    applied_user_id = Column(Integer, nullable=True, comment="应用人ID")
    applied_at = Column(DateTime, nullable=True, comment="应用时间")
    apply_detail = Column(JSON, nullable=True, comment="应用结果明细: [{target_type,target_id,action,before,after}]")
    created_at = Column(DateTime, server_default=func.now())
    updated_at = Column(DateTime, server_default=func.now(), onupdate=func.now())
