"""自动数据质量监控 — 任务3
定期检查KPI值异常、连续持平、数据缺失等
"""
from fastapi import APIRouter, Depends, HTTPException, Query
from sqlalchemy.orm import Session
from sqlalchemy import func, and_
from typing import Optional
from datetime import datetime, timedelta
import json
import logging

from app.database import get_db
from app.auth_middleware import require_auth, require_role
from app.models import KPIDefinition, KPIValue, KpiDataQualityLog, OperationLog
from app.api.kpis import kpi_to_dict

logger = logging.getLogger("data-quality")

router = APIRouter(prefix="/api/cma/data-quality", tags=["数据质量"],
    dependencies=[Depends(require_role("ceo", "finance", "business", "it"))],
)
WRITE_ROLES = Depends(require_role("ceo", "finance", "it"))


def _log_to_dict(log):
    d = {c.name: getattr(log, c.name) for c in log.__table__.columns}
    if hasattr(log, 'kpi') and log.kpi:
        d["kpi_code"] = log.kpi.kpi_code
        d["kpi_name"] = log.kpi.kpi_name
    return d


# ============================================================
# 质量检查
# ============================================================

@router.get("/check")
def run_quality_check(db: Session = Depends(get_db)):
    """扫描全部KPI，生成数据质量报告"""
    kpis = db.query(KPIDefinition).filter(KPIDefinition.status == "active").all()
    issues = []
    current_period = datetime.now().strftime("%Y-%m")

    for kpi in kpis:
        # 获取最近12个月的值
        values = db.query(KPIValue).filter(
            KPIValue.kpi_id == kpi.id,
            KPIValue.actual_value.isnot(None),
        ).order_by(KPIValue.period.desc()).limit(12).all()

        # 1. 检查数据缺失
        if not values:
            issues.append({
                "kpi_id": kpi.id, "kpi_code": kpi.kpi_code, "kpi_name": kpi.kpi_name,
                "check_type": "missing_data",
                "severity": "critical",
                "detail": {"missing_months": 12, "latest_period": None, "total_values": 0},
                "suggestion": "请初始化KPI数据，建议导入至少3个月历史数据",
            })
            continue

        latest_val = values[0]
        latest_period = latest_val.period

        # 计算缺失月数
        if latest_period:
            try:
                lp_parts = latest_period.split("-")
                lp_date = datetime(int(lp_parts[0]), int(lp_parts[1]), 1)
                now_date = datetime.now().replace(day=1)
                missing_months = max(0, (now_date.year - lp_date.year) * 12 + (now_date.month - lp_date.month) - 1)
                if missing_months > 1:
                    issues.append({
                        "kpi_id": kpi.id, "kpi_code": kpi.kpi_code, "kpi_name": kpi.kpi_name,
                        "check_type": "missing_data",
                        "severity": "warning" if missing_months <= 3 else "critical",
                        "detail": {"missing_months": missing_months, "latest_period": latest_period, "total_values": len(values)},
                        "suggestion": f"数据缺失{missing_months}个月，建议从ERP系统同步或手动补录",
                    })
            except Exception:
                pass

        # 2. 检查环比骤变（需要至少2个月的值）
        if len(values) >= 2 and latest_val.actual_value:
            prev_val = values[1].actual_value
            if prev_val and prev_val != 0:
                change_pct = abs((latest_val.actual_value - prev_val) / prev_val * 100)
                if change_pct > 50:
                    issues.append({
                        "kpi_id": kpi.id, "kpi_code": kpi.kpi_code, "kpi_name": kpi.kpi_name,
                        "check_type": "abnormal_change",
                        "severity": "warning" if change_pct <= 100 else "critical",
                        "detail": {
                            "change_pct": round(change_pct, 1),
                            "current_value": latest_val.actual_value,
                            "previous_value": prev_val,
                            "current_period": latest_val.period,
                            "previous_period": values[1].period,
                        },
                        "suggestion": f"环比变化{round(change_pct,1)}%，建议核实数据是否录入错误",
                    })

        # 3. 检查连续3期持平
        if len(values) >= 3:
            last_3 = [v.actual_value for v in values[:3] if v.actual_value is not None]
            if len(last_3) >= 3 and len(set(last_3)) == 1:
                issues.append({
                    "kpi_id": kpi.id, "kpi_code": kpi.kpi_code, "kpi_name": kpi.kpi_name,
                    "check_type": "flat_data",
                    "severity": "warning",
                    "detail": {"flat_value": last_3[0], "periods": [v.period for v in values[:3]]},
                    "suggestion": "连续3期数据完全相同，请确认数据源是否正常更新",
                })

        # 4. 检查值异常（偏离历史均值超过3倍标准差）
        if len(values) >= 4 and latest_val.actual_value:
            hist_vals = [v.actual_value for v in values[1:] if v.actual_value is not None]
            if len(hist_vals) >= 3:
                mean_val = sum(hist_vals) / len(hist_vals)
                variance = sum((v - mean_val) ** 2 for v in hist_vals) / len(hist_vals)
                stddev = variance ** 0.5 if variance > 0 else mean_val * 0.1
                if stddev > 0 and abs(latest_val.actual_value - mean_val) > 3 * stddev:
                    issues.append({
                        "kpi_id": kpi.id, "kpi_code": kpi.kpi_code, "kpi_name": kpi.kpi_name,
                        "check_type": "value_outlier",
                        "severity": "warning",
                        "detail": {
                            "current_value": latest_val.actual_value,
                            "mean": round(mean_val, 2),
                            "stddev": round(stddev, 2),
                            "z_score": round(abs(latest_val.actual_value - mean_val) / stddev, 2),
                        },
                        "suggestion": "当前值偏离历史均值超过3倍标准差，建议核实",
                    })

    # 写入质量日志
    created_count = 0
    for issue in issues:
        existing = db.query(KpiDataQualityLog).filter(
            KpiDataQualityLog.kpi_id == issue["kpi_id"],
            KpiDataQualityLog.check_type == issue["check_type"],
            KpiDataQualityLog.status == "open",
        ).first()
        if not existing:
            log = KpiDataQualityLog(
                kpi_id=issue["kpi_id"],
                check_type=issue["check_type"],
                severity=issue["severity"],
                detail=issue["detail"],
                suggestion=issue["suggestion"],
                status="open",
            )
            db.add(log)
            created_count += 1

    db.commit()
    return {
        "total_kpis": len(kpis),
        "issues_found": len(issues),
        "new_logs": created_count,
        "issues": issues,
    }


# ============================================================
# 质量日志CRUD
# ============================================================

@router.get("/logs")
def list_quality_logs(
    kpi_id: Optional[int] = None,
    severity: Optional[str] = None,
    check_type: Optional[str] = None,
    status: Optional[str] = None,
    db: Session = Depends(get_db),
):
    """获取数据质量日志"""
    query = db.query(KpiDataQualityLog)
    if kpi_id:
        query = query.filter(KpiDataQualityLog.kpi_id == kpi_id)
    if severity:
        query = query.filter(KpiDataQualityLog.severity == severity)
    if check_type:
        query = query.filter(KpiDataQualityLog.check_type == check_type)
    if status:
        query = query.filter(KpiDataQualityLog.status == status)

    logs = query.order_by(KpiDataQualityLog.created_at.desc()).limit(100).all()
    result = []
    for log in logs:
        d = _log_to_dict(log)
        kpi = db.query(KPIDefinition).filter(KPIDefinition.id == log.kpi_id).first()
        if kpi:
            d["kpi_code"] = kpi.kpi_code
            d["kpi_name"] = kpi.kpi_name
        result.append(d)
    return {"data": result, "total": len(result)}


@router.put("/logs/{log_id}")
def update_quality_log(log_id: int, data: dict, db: Session = Depends(get_db), user=WRITE_ROLES):
    """更新质量日志（解决/忽略）"""
    log = db.query(KpiDataQualityLog).filter(KpiDataQualityLog.id == log_id).first()
    if not log:
        raise HTTPException(404, "日志不存在")
    if "status" in data:
        log.status = data["status"]
        if data["status"] == "resolved":
            log.resolved_at = datetime.now()
    if "suggestion" in data:
        log.suggestion = data["suggestion"]
    db.commit()
    return _log_to_dict(log)


@router.delete("/logs/{log_id}")
def delete_quality_log(log_id: int, db: Session = Depends(get_db), user=WRITE_ROLES):
    log = db.query(KpiDataQualityLog).filter(KpiDataQualityLog.id == log_id).first()
    if log:
        db.delete(log)
        db.commit()
    return {"message": "已删除"}


# ============================================================
# 数据质量看板统计
# ============================================================

@router.get("/stats")
def quality_stats(db: Session = Depends(get_db)):
    """数据质量统计"""
    total_kpis = db.query(KPIDefinition).filter(KPIDefinition.status == "active").count()
    total_logs = db.query(KpiDataQualityLog).count()
    open_logs = db.query(KpiDataQualityLog).filter(KpiDataQualityLog.status == "open").count()

    # 按严重程度统计
    severity_counts = {}
    for s in ("info", "warning", "critical"):
        cnt = db.query(KpiDataQualityLog).filter(
            KpiDataQualityLog.severity == s,
            KpiDataQualityLog.status == "open",
        ).count()
        if cnt:
            severity_counts[s] = cnt

    # 按检查类型统计
    type_counts = {}
    for t in ("abnormal_change", "flat_data", "missing_data", "value_outlier"):
        cnt = db.query(KpiDataQualityLog).filter(
            KpiDataQualityLog.check_type == t,
            KpiDataQualityLog.status == "open",
        ).count()
        if cnt:
            type_counts[t] = cnt

    # ── 数据审计看板统计 ──
    # KPI完整度评分
    all_kpis = db.query(KPIDefinition).filter(KPIDefinition.status == "active").all()
    total = len(all_kpis)
    complete_kpis = 0
    missing_metadata_count = 0
    missing_data_count = 0
    stale_data_count = 0

    from datetime import datetime, timedelta
    six_months_ago = datetime.now() - timedelta(days=180)

    for kpi in all_kpis:
        # 元数据完整度检查
        has_meta = all([
            kpi.formula and kpi.formula.strip(),
            kpi.data_source and kpi.data_source.strip(),
            kpi.data_owner and kpi.data_owner.strip(),
            kpi.unit and kpi.unit.strip(),
            kpi.target_value is not None,
        ])
        if has_meta:
            complete_kpis += 1
        else:
            missing_metadata_count += 1

        # 数据缺失检查（是否有实际值）
        val = db.query(KPIValue).filter(
            KPIValue.kpi_id == kpi.id,
            KPIValue.actual_value.isnot(None),
        ).first()
        if not val:
            missing_data_count += 1

        # 超30天未更新预警
        latest_val = db.query(KPIValue).filter(
            KPIValue.kpi_id == kpi.id,
            KPIValue.actual_value.isnot(None),
        ).order_by(KPIValue.period.desc()).first()
        if latest_val and latest_val.calculated_at:
            if latest_val.calculated_at < six_months_ago:
                stale_data_count += 1

    completeness_score = round(complete_kpis / total * 100, 1) if total > 0 else 0
    missing_rate = round(missing_data_count / total * 100, 1) if total > 0 else 0

    return {
        "total_kpis": total_kpis,
        "total_logs": total_logs,
        "open_logs": open_logs,
        "severity_counts": severity_counts,
        "type_counts": type_counts,
        # 数据审计看板
        "completeness": {
            "score": completeness_score,
            "complete": complete_kpis,
            "total": total,
            "missing_metadata": missing_metadata_count,
        },
        "data_missing": {
            "count": missing_data_count,
            "rate": missing_rate,
            "total": total,
        },
        "stale_data": {
            "count": stale_data_count,
            "threshold_days": 180,
        },
    }
