# -*- coding: utf-8 -*-
"""
statistics_service.py — 监测统计聚合

提供统计 API：
  - get_overview: 概览摘要
  - get_module_stats: 模块健康统计
  - get_item_trend: 检测项趋势
  - get_abnormal_items: 高频异常项
  - get_item_keys_for_target: 检测项键列表
"""

from __future__ import annotations

import logging
from collections import defaultdict
from datetime import datetime, timedelta
from typing import Dict, List, Optional, Tuple

from . import report_service, target_service
from ..utils.thresholds import _extract_numeric, THRESHOLDS
from ..utils.display_names import DISPLAY_NAMES

logger = logging.getLogger("service_monitor.statistics")

NORMAL, WARNING, CRITICAL = "正常", "警告", "严重"
DEFAULT_RANGE_DAYS = 30
MAX_REPORTS_LOAD = 200


def _parse_date_filter(
    target_id: Optional[str],
    start_date: Optional[str],
    end_date: Optional[str],
) -> Tuple[Optional[str], str, str]:
    """验证并规范化日期筛选参数。

    Returns:
        (target_id, start_date_iso, end_date_iso)
        默认值：start = 30 天前，end = 今天
    """
    # 目标：空字符串转 None
    target_id = target_id or None

    # 日期范围
    today = datetime.now().date()
    default_start = today - timedelta(days=DEFAULT_RANGE_DAYS)

    if start_date:
        try:
            start_dt = datetime.strptime(start_date, "%Y-%m-%d").date()
        except ValueError:
            start_dt = default_start
    else:
        start_dt = default_start

    if end_date:
        try:
            end_dt = datetime.strptime(end_date, "%Y-%m-%d").date()
        except ValueError:
            end_dt = today
    else:
        end_dt = today

    # 确保 start <= end
    if start_dt > end_dt:
        start_dt, end_dt = end_dt, start_dt

    return target_id, start_dt.isoformat(), end_dt.isoformat()


def _load_reports_in_range(
    target_id: Optional[str],
    start_date: str,
    end_date: str,
    limit: int = MAX_REPORTS_LOAD,
) -> List[dict]:
    """加载日期范围内的完整报告数据。

    使用 list_reports() 先进行轻量级过滤，然后仅加载需要的完整报告。
    返回按 started_at 升序排序的完整报告字典列表。
    """
    # 获取摘要列表
    summaries = report_service.list_reports(target_id=target_id, limit=limit)

    # 解析日期范围
    try:
        start_dt = datetime.strptime(start_date, "%Y-%m-%d")
        end_dt = datetime.strptime(end_date, "%Y-%m-%d") + timedelta(days=1)  # 包含当天
    except ValueError:
        return []

    # 过滤日期范围内的报告
    matched_ids = []
    for s in summaries:
        started_at = s.get("started_at", "")
        if not started_at:
            continue
        try:
            report_dt = datetime.strptime(started_at[:19], "%Y-%m-%dT%H:%M:%S")
            if start_dt <= report_dt < end_dt:
                matched_ids.append(s["id"])
        except ValueError:
            continue

    # 加载完整报告
    reports = []
    for rid in matched_ids:
        report = report_service.get_report(rid)
        if report:
            reports.append(report)

    # 按 started_at 升序排序
    reports.sort(key=lambda r: r.get("started_at", ""))

    return reports


def get_overview(
    target_id: Optional[str],
    start_date: Optional[str],
    end_date: Optional[str],
) -> dict:
    """计算概览统计数据。

    Returns:
        {
            "total_reports": int,
            "total_items": int,
            "normal_count": int,
            "warning_count": int,
            "critical_count": int,
            "abnormal_rate": float,
            "most_abnormal_item": {"key": str, "name": str, "abnormal_count": int} or None,
            "date_range": {"start": str, "end": str},
        }
    """
    target_id, start_iso, end_iso = _parse_date_filter(target_id, start_date, end_date)
    reports = _load_reports_in_range(target_id, start_iso, end_iso)

    total_reports = len(reports)
    total_items = 0
    normal_count = 0
    warning_count = 0
    critical_count = 0

    # 异常项计数器
    abnormal_counter: Dict[str, dict] = defaultdict(lambda: {"name": "", "count": 0})

    for report in reports:
        summary = report.get("summary", {})
        total_items += summary.get("total", 0)
        normal_count += summary.get(NORMAL, 0)
        warning_count += summary.get(WARNING, 0)
        critical_count += summary.get(CRITICAL, 0)

        # 统计异常项
        for module in report.get("modules", []):
            for item in module.get("items", []):
                status = item.get("status", NORMAL)
                if status in (WARNING, CRITICAL):
                    key = item.get("key", "")
                    name = item.get("name", key)
                    abnormal_counter[key]["name"] = name
                    abnormal_counter[key]["count"] += 1

    # 计算异常率
    abnormal_rate = 0.0
    if total_items > 0:
        abnormal_rate = round((warning_count + critical_count) / total_items * 100, 2)

    # 找出最常异常项
    most_abnormal = None
    if abnormal_counter:
        top_key = max(abnormal_counter.keys(), key=lambda k: abnormal_counter[k]["count"])
        most_abnormal = {
            "key": top_key,
            "name": abnormal_counter[top_key]["name"],
            "abnormal_count": abnormal_counter[top_key]["count"],
        }

    return {
        "total_reports": total_reports,
        "total_items": total_items,
        "normal_count": normal_count,
        "warning_count": warning_count,
        "critical_count": critical_count,
        "abnormal_rate": abnormal_rate,
        "most_abnormal_item": most_abnormal,
        "date_range": {"start": start_iso, "end": end_iso},
    }


def get_module_stats(
    target_id: Optional[str],
    start_date: Optional[str],
    end_date: Optional[str],
) -> dict:
    """计算模块健康统计。

    Returns:
        {
            "modules": [
                {
                    "id": str,
                    "name": str,
                    "category": str,
                    "total": int,
                    "normal": int,
                    "warning": int,
                    "critical": int,
                    "pass_rate": float,
                },
                ...
            ]
        }
    """
    target_id, start_iso, end_iso = _parse_date_filter(target_id, start_date, end_date)
    reports = _load_reports_in_range(target_id, start_iso, end_iso)

    # 按模块 ID 聚合
    module_stats: Dict[str, dict] = defaultdict(lambda: {
        "name": "",
        "category": "",
        "total": 0,
        "normal": 0,
        "warning": 0,
        "critical": 0,
    })

    for report in reports:
        for module in report.get("modules", []):
            mod_id = module.get("id", "")
            mod_name = module.get("name", mod_id)
            mod_category = module.get("category", "system")
            mod_summary = module.get("summary", {})

            module_stats[mod_id]["name"] = mod_name
            module_stats[mod_id]["category"] = mod_category
            module_stats[mod_id]["total"] += mod_summary.get("total", 0)
            module_stats[mod_id]["normal"] += mod_summary.get(NORMAL, 0)
            module_stats[mod_id]["warning"] += mod_summary.get(WARNING, 0)
            module_stats[mod_id]["critical"] += mod_summary.get(CRITICAL, 0)

    # 构建结果列表
    modules = []
    for mod_id, stats in module_stats.items():
        total = stats["total"]
        pass_rate = round(stats["normal"] / total * 100, 2) if total > 0 else 0.0
        modules.append({
            "id": mod_id,
            "name": stats["name"],
            "category": stats["category"],
            "total": total,
            "normal": stats["normal"],
            "warning": stats["warning"],
            "critical": stats["critical"],
            "pass_rate": pass_rate,
        })

    # 按类别排序：system 在前，service 在后
    modules.sort(key=lambda m: (0 if m["category"] == "system" else 1, m["name"]))

    return {"modules": modules}


def get_item_trend(
    target_id: Optional[str],
    item_key: str,
    start_date: Optional[str],
    end_date: Optional[str],
) -> dict:
    """计算检测项趋势数据。

    Returns:
        {
            "item_key": str,
            "item_name": str,
            "unit": str,
            "threshold": str,
            "data_points": [
                {"time": str, "value_num": float or None, "value_raw": str, "status": str},
                ...
            ],
            "status_distribution": {"正常": int, "警告": int, "严重": int},
        }
    """
    target_id, start_iso, end_iso = _parse_date_filter(target_id, start_date, end_date)
    reports = _load_reports_in_range(target_id, start_iso, end_iso)

    data_points = []
    status_dist = {NORMAL: 0, WARNING: 0, CRITICAL: 0}
    item_name = DISPLAY_NAMES.get(item_key, item_key)
    unit = ""
    threshold = THRESHOLDS.get(item_key, "")

    for report in reports:
        time_str = report.get("started_at", "")
        found = False

        for module in report.get("modules", []):
            for item in module.get("items", []):
                if item.get("key") == item_key:
                    value_raw = item.get("value", "")
                    status = item.get("status", NORMAL)
                    status_dist[status] += 1

                    # 提取数值
                    value_num, extracted_unit = _extract_numeric(value_raw)
                    if extracted_unit and not unit:
                        unit = extracted_unit

                    data_points.append({
                        "time": time_str,
                        "value_num": value_num,
                        "value_raw": value_raw,
                        "status": status,
                    })
                    found = True
                    break
            if found:
                break

    return {
        "item_key": item_key,
        "item_name": item_name,
        "unit": unit,
        "threshold": threshold,
        "data_points": data_points,
        "status_distribution": status_dist,
    }


def get_health_trend(
    target_id: Optional[str],
    start_date: Optional[str],
    end_date: Optional[str],
) -> dict:
    """计算健康度趋势数据。

    每份报告的健康度评分公式：
      score = (normal_count * 100 + warning_count * 60 + critical_count * 0) / total_items

    Returns:
        {
            "data_points": [
                {
                    "date": str,          # 报告日期（YYYY-MM-DD）
                    "score": float,       # 健康度评分 0-100
                    "normal_count": int,
                    "warning_count": int,
                    "critical_count": int,
                    "total_items": int,
                },
                ...
            ],
            "overall_score": float,      # 整体平均分
            "date_range": {"start": str, "end": str},
        }
    """
    target_id, start_iso, end_iso = _parse_date_filter(target_id, start_date, end_date)
    reports = _load_reports_in_range(target_id, start_iso, end_iso)

    data_points = []
    total_score = 0.0
    scored_count = 0

    for report in reports:
        summary = report.get("summary", {})
        total_items = summary.get("total", 0)
        n = summary.get(NORMAL, 0)
        w = summary.get(WARNING, 0)
        c = summary.get(CRITICAL, 0)

        if total_items > 0:
            score = round((n * 100 + w * 60 + c * 0) / total_items, 2)
        else:
            score = 100.0  # 无检测项视为满分

        # 取报告日期（按天聚合）
        started_at = report.get("started_at", "")
        date_key = started_at[:10] if started_at else ""

        data_points.append({
            "date": date_key,
            "score": score,
            "normal_count": n,
            "warning_count": w,
            "critical_count": c,
            "total_items": total_items,
        })
        total_score += score
        scored_count += 1

    overall_score = round(total_score / scored_count, 2) if scored_count > 0 else 100.0

    return {
        "data_points": data_points,
        "overall_score": overall_score,
        "date_range": {"start": start_iso, "end": end_iso},
    }


def get_abnormal_items(
    target_id: Optional[str],
    start_date: Optional[str],
    end_date: Optional[str],
    top_n: int = 10,
) -> dict:
    """计算高频异常检测项。

    Returns:
        {
            "items": [
                {
                    "key": str,
                    "name": str,
                    "module_name": str,
                    "category": str,
                    "total_appearances": int,
                    "abnormal_count": int,
                    "abnormal_rate": float,
                    "warning_count": int,
                    "critical_count": int,
                },
                ...
            ]
        }
    """
    target_id, start_iso, end_iso = _parse_date_filter(target_id, start_date, end_date)
    reports = _load_reports_in_range(target_id, start_iso, end_iso)

    # 按检测项统计
    item_stats: Dict[str, dict] = defaultdict(lambda: {
        "name": "",
        "module_name": "",
        "category": "",
        "total": 0,
        "warning": 0,
        "critical": 0,
    })

    for report in reports:
        for module in report.get("modules", []):
            mod_name = module.get("name", "")
            mod_category = module.get("category", "system")
            for item in module.get("items", []):
                key = item.get("key", "")
                if not key:
                    continue
                status = item.get("status", NORMAL)

                item_stats[key]["name"] = item.get("name", key)
                item_stats[key]["module_name"] = mod_name
                item_stats[key]["category"] = mod_category
                item_stats[key]["total"] += 1

                if status == WARNING:
                    item_stats[key]["warning"] += 1
                elif status == CRITICAL:
                    item_stats[key]["critical"] += 1

    # 计算异常率并排序
    items = []
    for key, stats in item_stats.items():
        abnormal_count = stats["warning"] + stats["critical"]
        if abnormal_count == 0:
            continue  # 跳过无异常的项

        abnormal_rate = round(abnormal_count / stats["total"] * 100, 2) if stats["total"] > 0 else 0.0
        items.append({
            "key": key,
            "name": stats["name"],
            "module_name": stats["module_name"],
            "category": stats["category"],
            "total_appearances": stats["total"],
            "abnormal_count": abnormal_count,
            "abnormal_rate": abnormal_rate,
            "warning_count": stats["warning"],
            "critical_count": stats["critical"],
        })

    # 按异常次数降序排序，取 TOP N
    items.sort(key=lambda x: x["abnormal_count"], reverse=True)
    items = items[:top_n]

    return {"items": items}


def get_item_keys_for_target(
    target_id: Optional[str],
    start_date: Optional[str],
    end_date: Optional[str],
) -> List[dict]:
    """列出目标在日期范围内出现过的所有检测项键（按模块分组）。

    用于填充检测项选择器下拉菜单（使用 optgroup 分组）。

    Returns:
        [
            {
                "module_id": str,
                "module_name": str,
                "category": str,
                "items": [
                    {"key": str, "name": str},
                    ...
                ]
            },
            ...
        ]
    """
    target_id, start_iso, end_iso = _parse_date_filter(target_id, start_date, end_date)
    reports = _load_reports_in_range(target_id, start_iso, end_iso)

    # 按模块收集检测项
    modules_map: Dict[str, dict] = {}

    for report in reports:
        for module in report.get("modules", []):
            mod_id = module.get("id", "")
            mod_name = module.get("name", mod_id)
            mod_category = module.get("category", "system")

            if mod_id not in modules_map:
                modules_map[mod_id] = {
                    "module_id": mod_id,
                    "module_name": mod_name,
                    "category": mod_category,
                    "items": {},
                }

            for item in module.get("items", []):
                key = item.get("key", "")
                if not key:
                    continue

                if key not in modules_map[mod_id]["items"]:
                    # 检查该值是否可提取数值
                    value_raw = item.get("value", "")
                    try:
                        value_num, _ = _extract_numeric(value_raw)
                        has_numeric = value_num is not None
                    except Exception:
                        has_numeric = False
                    modules_map[mod_id]["items"][key] = {
                        "key": key,
                        "name": item.get("name", key),
                        "has_numeric": has_numeric,
                    }
                elif not modules_map[mod_id]["items"][key].get("has_numeric"):
                    # 尝试更新：之前无数值，再检查一次
                    value_raw = item.get("value", "")
                    try:
                        value_num, _ = _extract_numeric(value_raw)
                        if value_num is not None:
                            modules_map[mod_id]["items"][key]["has_numeric"] = True
                    except Exception:
                        pass

    # 转换为列表格式
    result = []
    for mod_id, mod_data in modules_map.items():
        items = list(mod_data["items"].values())
        if items:
            items.sort(key=lambda x: x["name"])
            result.append({
                "module_id": mod_id,
                "module_name": mod_data["module_name"],
                "category": mod_data["category"],
                "items": items,  # 每个 item 包含 key, name, has_numeric
            })

    # 按类别和模块名排序：system 在前，service 在后
    result.sort(key=lambda m: (0 if m["category"] == "system" else 1, m["module_name"]))

    return result
