"""改善行动计划 API — 管理会计OS"""
from fastapi import APIRouter, Depends, HTTPException
from sqlalchemy.orm import Session
from datetime import datetime, timezone
from typing import Optional
import re
import logging
from calendar import monthrange
from app.database import get_db
from app.deps import get_entity_id
from app.auth_middleware import require_role, require_auth
from app.models import ActionPlan, KPIAlert, KPIDefinition, KPIValue, User, Objective, KR
from app.api.verify import build_auto_verify_rule

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

router = APIRouter(prefix="/api/cma/action-plans", tags=["改善行动"],
    dependencies=[Depends(require_role("ceo", "finance", "business", "it"))],
)


# ──────────────────────────────────────────────
# 工具函数
# ──────────────────────────────────────────────

def _quarter_to_date_range(quarter: str) -> tuple:
    """解析季度字符串 '2026Q3' → (start_date, end_date)"""
    m = re.match(r"^(\d{4})[Qq]([1-4])$", quarter.strip())
    if not m:
        return None, None
    year = int(m.group(1))
    q = int(m.group(2))
    month_map = {1: (1, 1), 2: (4, 1), 3: (7, 1), 4: (10, 1)}
    start_month, start_day = month_map[q]
    end_month = start_month + 2
    if end_month > 12:
        end_month -= 12
        end_year = year + 1
    else:
        end_year = year
    _, last_day = monthrange(end_year, end_month)
    return (
        datetime(year, start_month, start_day, tzinfo=timezone.utc),
        datetime(end_year, end_month, last_day, 23, 59, 59, tzinfo=timezone.utc),
    )


def _validate_due_date_against_quarter(due_date: datetime, quarter: str):
    """校验截止日期是否在季度范围内，不匹配则抛422"""
    q_start, q_end = _quarter_to_date_range(quarter)
    if q_start is None:
        return  # 无法解析季度，跳过校验
    due = due_date if due_date.tzinfo else due_date.replace(tzinfo=timezone.utc)
    if due < q_start:
        raise HTTPException(422,
            f"KR截止日期({due.date()})早于本季度开始({q_start.date()})，请检查")
    if due > q_end:
        raise HTTPException(422,
            f"KR截止日期({due.date()})超出本季度范围({q_end.date()})，最大截止为{q_end.date()}")


def plan_to_dict(p: ActionPlan) -> dict:
    return {
        "id": p.id,
        "alert_id": p.alert_id,
        "kpi_id": p.kpi_id,
        "objective_id": p.objective_id,
        "kr_id": p.kr_id,
        "title": p.title,
        "description": p.description,
        "assignee": p.assignee,
        "priority": p.priority,
        "due_date": p.due_date.isoformat() if p.due_date else None,
        "status": p.status,
        "progress": p.progress or 0,
        "result": p.result,
        "created_by": p.created_by,
        "created_at": p.created_at.isoformat() if p.created_at else None,
        "updated_at": p.updated_at.isoformat() if p.updated_at else None,
    }


# ──────────────────────────────────────────────
# API 端点
# ──────────────────────────────────────────────

@router.get("")
def list_plans(
    status: Optional[str] = None,
    kpi_id: Optional[int] = None,
    alert_id: Optional[int] = None,
    keyword: Optional[str] = None,
    db: Session = Depends(get_db),
    current_user: User = Depends(require_auth),
    entity_id: int = Depends(get_entity_id),
):
    """获取行动计划列表（账套隔离: join KPI按企业过滤, 2026-08-23 P1b）"""
    query = db.query(ActionPlan).join(KPIDefinition, KPIDefinition.id == ActionPlan.kpi_id).filter(KPIDefinition.entity_id == entity_id).order_by(ActionPlan.created_at.desc())

    if status:
        query = query.filter(ActionPlan.status == status)
    if kpi_id:
        query = query.filter(ActionPlan.kpi_id == kpi_id)
    if alert_id:
        query = query.filter(ActionPlan.alert_id == alert_id)
    if keyword:
        query = query.filter(ActionPlan.title.like(f"%{keyword}%"))

    # business角色只看自己的
    if current_user.role == "business":
        query = query.filter(
            (ActionPlan.assignee == current_user.username) |
            (ActionPlan.assignee == current_user.name)
        )

    plans = query.all()
    result = []
    for p in plans:
        item = plan_to_dict(p)
        # 附带KPI名称
        kpi = db.query(KPIDefinition).filter(KPIDefinition.id == p.kpi_id).first()
        item["kpi_name"] = kpi.kpi_name if kpi else "未知KPI"
        item["kpi_code"] = kpi.kpi_code if kpi else None
        item["kpi_dimension"] = kpi.dimension if kpi else None
        # 附带KR信息（行动挂KR 2026-08-27）
        if p.kr_id is not None:
            kr = db.query(KR).filter(KR.id == p.kr_id).first()
            item["kr_title"] = kr.title if kr else None
            item["kr_metric_kpi_id"] = kr.metric_kpi_id if kr else None
        else:
            item["kr_title"] = None
            item["kr_metric_kpi_id"] = None
        result.append(item)

    return {"data": result}


@router.post("")
def create_plan(
    data: dict,
    db: Session = Depends(get_db),
    current_user: User = Depends(require_auth),
    entity_id: int = Depends(get_entity_id),
):
    """创建改善行动计划（也是OKR的KR）"""
    required = ["title", "kpi_id"]
    for field in required:
        if field not in data:
            raise HTTPException(400, f"缺少必填字段: {field}")
    # 账套隔离: 关联KPI必须属于当前企业 (2026-08-23 P1b)
    kpi_ent = db.query(KPIDefinition).filter(KPIDefinition.id == data["kpi_id"]).first()
    if not kpi_ent or kpi_ent.entity_id != entity_id:
        raise HTTPException(404, "关联KPI不存在")

    # 行动挂KR (2026-08-27): kr_id 需存在且属于当前企业
    kr_id = data.get("kr_id")
    if kr_id:
        kr_ent = db.query(KR).filter(KR.id == kr_id, KR.entity_id == entity_id).first()
        if not kr_ent:
            raise HTTPException(404, "关联KR不存在")
        # 未显式传objective_id时从KR继承目标
        if not data.get("objective_id"):
            data["objective_id"] = kr_ent.objective_id

    due_date = datetime.fromisoformat(data["due_date"]) if data.get("due_date") else None

    # 校验截止日期与关联Objective的季度匹配
    objective_id = data.get("objective_id")
    if objective_id and due_date:
        obj = db.query(Objective).filter(Objective.id == objective_id).first()
        if obj and obj.quarter:
            _validate_due_date_against_quarter(due_date, obj.quarter)

    plan = ActionPlan(
        alert_id=data.get("alert_id"),
        kpi_id=data["kpi_id"],
        objective_id=objective_id,
        kr_id=kr_id,
        title=data["title"],
        description=data.get("description"),
        assignee=data.get("assignee"),
        priority=data.get("priority", "medium"),
        due_date=due_date,
        status="pending",
        progress=0,
        created_by=current_user.name or current_user.username,
        auto_verify_rule=data.get("auto_verify_rule"),  # 显式规则原样保存；None 时下面自动生成
    )
    # 第三阶段接入（2026-08-31）：创建 ActionPlan 自动生成验证规则
    # 请求体未传 auto_verify_rule 且关联 KPI 存在 → 复用 verify.build_auto_verify_rule 生成默认规则（不改变现有创建行为；auto_close 默认 false）
    if not data.get("auto_verify_rule"):
        baseline_value = data.get("baseline_value")
        if baseline_value is None:
            # 基线缺省时取 KPI 当前最新值（period <= 当前月），供 kpi_current_before 回填
            latest_kpi = db.query(KPIValue).filter(
                KPIValue.kpi_id == kpi_ent.id,
                KPIValue.actual_value.isnot(None),
                KPIValue.period <= datetime.now().strftime("%Y-%m"),
            ).order_by(KPIValue.period.desc(), KPIValue.calculated_at.desc(), KPIValue.id.desc()).first()
            if latest_kpi:
                baseline_value = latest_kpi.actual_value
        plan.auto_verify_rule = build_auto_verify_rule(kpi_ent, baseline_value=baseline_value)
    db.add(plan)
    db.commit()
    db.refresh(plan)

    # OKR时间分解：KR创建时自动生成3个月度里程碑（按截止日期向前均分）
    if due_date and not plan.monthly_milestones:
        try:
            plan.monthly_milestones = _auto_build_milestones(due_date)
            db.commit()
        except Exception:
            db.rollback()  # 里程碑生成失败不影响KR创建
            db.refresh(plan)
    return plan_to_dict(plan)


def _auto_build_milestones(due_date: datetime) -> list:
    """按截止日期向前均分3个月度里程碑"""
    base = due_date.replace(day=1)
    month_keys = []
    for i in range(3, 0, -1):
        y, m = base.year, base.month - i
        while m <= 0:
            m += 12
            y -= 1
        month_keys.append(f"{y:04d}-{m:02d}")
    return [
        {
            "month": mk,
            "label": f"里程碑{i + 1}",
            "status": "pending",
            "target": None,
        }
        for i, mk in enumerate(month_keys)
    ]


@router.put("/{plan_id}")
def update_plan(
    plan_id: int,
    data: dict,
    db: Session = Depends(get_db),
):
    """更新行动计划"""
    plan = db.query(ActionPlan).filter(ActionPlan.id == plan_id).first()
    if not plan:
        raise HTTPException(404, "计划不存在")

    if "title" in data:
        plan.title = data["title"]
    if "description" in data:
        plan.description = data["description"]
    if "assignee" in data:
        plan.assignee = data["assignee"]
    if "priority" in data:
        plan.priority = data["priority"]
    if "kr_id" in data:
        # 行动挂KR: 支持置空(null)或改挂
        if data["kr_id"] is None:
            plan.kr_id = None
        else:
            kr_ent = db.query(KR).filter(KR.id == data["kr_id"]).first()
            if not kr_ent:
                raise HTTPException(404, "关联KR不存在")
            plan.kr_id = kr_ent.id
            if data.get("objective_id") is not None:
                plan.objective_id = data["objective_id"]
            elif plan.objective_id is None:
                plan.objective_id = kr_ent.objective_id
    if "objective_id" in data:
        plan.objective_id = data["objective_id"]
    if "due_date" in data:
        plan.due_date = datetime.fromisoformat(data["due_date"]) if data["due_date"] else None
    if "status" in data:
        plan.status = data["status"]
    if "progress" in data:
        plan.progress = max(0, min(100, data["progress"]))
    if "result" in data:
        plan.result = data["result"]

    db.commit()
    db.refresh(plan)
    return plan_to_dict(plan)


@router.delete("/{plan_id}")
def delete_plan(plan_id: int, db: Session = Depends(get_db)):
    """删除行动计划"""
    plan = db.query(ActionPlan).filter(ActionPlan.id == plan_id).first()
    if not plan:
        raise HTTPException(404, "计划不存在")
    db.delete(plan)
    db.commit()
    return {"message": "已删除"}


@router.get("/stats")
def plan_stats(db: Session = Depends(get_db), current_user: User = Depends(require_auth)):
    """行动计划统计"""
    query = db.query(ActionPlan)
    if current_user.role == "business":
        query = query.filter(
            (ActionPlan.assignee == current_user.username) |
            (ActionPlan.assignee == current_user.name)
        )
    total = query.count()
    pending = query.filter(ActionPlan.status == "pending").count()
    in_progress = query.filter(ActionPlan.status == "in_progress").count()
    completed = query.filter(ActionPlan.status == "completed").count()
    from datetime import datetime
    overdue = query.filter(ActionPlan.status.in_(["pending", "in_progress"]), ActionPlan.due_date < datetime.now()).count()
    return {
        "total": total,
        "pending": pending,
        "in_progress": in_progress,
        "completed": completed,
        "overdue": overdue,
    }


# ──────────────────────────────────────────────
# COSO内控自检表 (CMA P1 - COSO五要素)
# ──────────────────────────────────────────────

COSO_CHECKLIST_DATA = {
    "hanke": {
        "entity_name": "陕西酣客（白酒经销）",
        "total_score": 46,
        "max_score": 100,
        "risk_level": "high",
        "risk_label": "高风险",
        "elements": [
            {
                "id": "control_environment", "name": "控制环境", "name_en": "Control Environment",
                "score": 60, "max_score": 100, "status": "medium",
                "items": [
                    {"id": "ce_01", "text": "管理层重视内控", "passed": True, "detail": "✅ 任总亲自跟"},
                    {"id": "ce_02", "text": "职责分离", "passed": True, "detail": "✅ 业务≠财务"},
                    {"id": "ce_03", "text": "授权审批制度", "passed": False, "detail": "❌ 渠补无标准审批流程"},
                    {"id": "ce_04", "text": "人事政策", "passed": False, "detail": "❌ 无定期轮岗"},
                ],
            },
            {
                "id": "risk_assessment", "name": "风险评估", "name_en": "Risk Assessment",
                "score": 40, "max_score": 100, "status": "low",
                "items": [
                    {"id": "ra_01", "text": "风险识别机制", "passed": False, "detail": "❌ 没有系统风险清单"},
                    {"id": "ra_02", "text": "风险应对预案", "passed": False, "detail": "❌ 现金断流无预案"},
                ],
            },
            {
                "id": "control_activities", "name": "控制活动", "name_en": "Control Activities",
                "score": 30, "max_score": 100, "status": "low",
                "items": [
                    {"id": "ca_01", "text": "渠补审批流程", "passed": False, "detail": "❌ 口头谈，无记录"},
                    {"id": "ca_02", "text": "费用审批流程", "passed": False, "detail": "❌ 超预算无拦截"},
                    {"id": "ca_03", "text": "实物返利入账流程", "passed": False, "detail": "❌ 纯P&L不进系统"},
                ],
            },
            {
                "id": "information_communication", "name": "信息与沟通", "name_en": "Information & Communication",
                "score": 70, "max_score": 100, "status": "medium",
                "items": [
                    {"id": "ic_01", "text": "财务报告及时性", "passed": True, "detail": "✅ 月度出表"},
                    {"id": "ic_02", "text": "系统数据互通", "passed": False, "detail": "❌ 进销存≠财务账"},
                ],
            },
            {
                "id": "monitoring", "name": "监控", "name_en": "Monitoring",
                "score": 30, "max_score": 100, "status": "low",
                "items": [
                    {"id": "mo_01", "text": "定期内审", "passed": False, "detail": "❌ 无"},
                    {"id": "mo_02", "text": "异常追踪机制", "passed": False, "detail": "❌ 发现异常无跟踪"},
                ],
            },
        ],
    },
    "bohai": {
        "entity_name": "陕西博海科技（IT服务）",
        "total_score": 55,
        "max_score": 100,
        "risk_level": "medium",
        "risk_label": "中风险",
        "elements": [
            {
                "id": "control_environment", "name": "控制环境", "name_en": "Control Environment",
                "score": 70, "max_score": 100, "status": "medium",
                "items": [
                    {"id": "ce_01", "text": "管理层重视内控", "passed": True, "detail": "✅ 老板直接管"},
                    {"id": "ce_02", "text": "职责分离", "passed": True, "detail": "✅ 业务≠财务≠技术"},
                    {"id": "ce_03", "text": "授权审批制度", "passed": False, "detail": "❌ 部分项目无预算审批"},
                ],
            },
            {
                "id": "risk_assessment", "name": "风险评估", "name_en": "Risk Assessment",
                "score": 50, "max_score": 100, "status": "low",
                "items": [
                    {"id": "ra_01", "text": "风险识别机制", "passed": False, "detail": "❌ 无正式风险清单"},
                    {"id": "ra_02", "text": "风险应对预案", "passed": True, "detail": "✅ 重点项目有预案"},
                ],
            },
            {
                "id": "control_activities", "name": "控制活动", "name_en": "Control Activities",
                "score": 50, "max_score": 100, "status": "low",
                "items": [
                    {"id": "ca_01", "text": "采购审批流程", "passed": True, "detail": "✅ 有标准流程"},
                    {"id": "ca_02", "text": "项目交付流程", "passed": False, "detail": "❌ 验收流程不完善"},
                ],
            },
            {
                "id": "information_communication", "name": "信息与沟通", "name_en": "Information & Communication",
                "score": 60, "max_score": 100, "status": "medium",
                "items": [
                    {"id": "ic_01", "text": "财务报告及时性", "passed": True, "detail": "✅ 月度出表"},
                    {"id": "ic_02", "text": "项目沟通机制", "passed": False, "detail": "❌ 跨部门信息滞后"},
                ],
            },
            {
                "id": "monitoring", "name": "监控", "name_en": "Monitoring",
                "score": 40, "max_score": 100, "status": "low",
                "items": [
                    {"id": "mo_01", "text": "定期内审", "passed": False, "detail": "❌ 无"},
                    {"id": "mo_02", "text": "异常追踪机制", "passed": True, "detail": "✅ 项目延期有跟踪"},
                ],
            },
        ],
    },
}


@router.get("/coso-checklist")
def get_coso_checklist(entity: str = "hanke"):
    """COSO内控自检表 - CMA P1 COSO五要素"""
    data = COSO_CHECKLIST_DATA.get(entity)
    if not data:
        data = COSO_CHECKLIST_DATA["hanke"]
        data["entity_name"] = f"未知实体({entity})，默认返回酣客数据"
    return data
