# -*- coding: utf-8 -*-
"""
parser.py — 模块输出解析与状态判定

移植参考脚本的 Parse-ModuleResult / Get-StatusByValue / Get-DisplayName / Get-Threshold。

职责：
  1. 从 bash 模块的原始 stdout 提取 KEY:VALUE（过滤日志/报错/脏行）
  2. 关联显示名、阈值、状态等级
  3. 利用模块自带的 *_LEVEL 输出作为权威状态来源

状态等级：正常 / 警告 / 严重
"""

from __future__ import annotations

import re
from dataclasses import dataclass
from typing import Dict, List, Optional

from .display_names import DISPLAY_NAMES
from .thresholds import THRESHOLDS, judge_status

# 标准 KEY:VALUE 行：KEY 由大写字母/数字/下划线组成，英文冒号
_KV_RE = re.compile(r"^([A-Z_][A-Z0-9_]*)\s*:\s*(.+)$")

# 状态等级常量
NORMAL = "正常"
WARNING = "警告"
CRITICAL = "严重"

# 值文本 → 状态映射（移植 Get-StatusByValue 的状态词判定）
_VALUE_STATUS_MAP = {
    "正常": NORMAL, "OK": NORMAL, "运行中": NORMAL, "是": NORMAL,
    "警告": WARNING, "已停止": WARNING,
    "严重": CRITICAL, "ERROR": CRITICAL, "异常": CRITICAL, "未运行": CRITICAL,
}


@dataclass
class CheckItem:
    """单个检测项结果。"""
    key: str
    name: str            # 显示名
    value: str
    threshold: str        # 阈值描述，如 ">85%"，无则空
    status: str          # 正常/警告/严重
    module_id: str
    category: str


def _should_skip(line: str) -> bool:
    """判断是否为需过滤的脏行（移植 Parse-ModuleResult 的过滤规则）。"""
    if not line:
        return True
    # 注释、日志行
    if line.startswith("#") or line.startswith("["):
        return True
    # grep 警告
    if re.match(r"^grep.*警告", line):
        return True
    # bash 脚本报错行：xxx.sh: 行 N / xxx.sh: line N
    if re.search(r"\.sh.*行\s*\d+", line) or re.search(r"\.sh:\s*line\s*\d+", line):
        return True
    # 整数表达式报错
    if "需要整数表达式" in line or "语法错误" in line or "表达式中有语法错误" in line:
        return True
    # 数字|数字 形式的错误输出
    if re.match(r"^\d+\s*\|\s*\d+", line):
        return True
    # 中文冒号 + 方括号/竖线的错误输出
    if re.search(r"：\[", line) or re.search(r"：\d+\s*\|", line):
        return True
    # command not found 等
    if "command not found" in line or "未找到命令" in line:
        return True
    return False


def parse_module_output(raw: str, module_id: str, category: str) -> List[CheckItem]:
    """解析单个模块的原始输出为 CheckItem 列表。

    两阶段：
      1. 提取所有 KEY:VALUE，先建 raw_map（含 *_LEVEL 字段）
      2. 为每个非 *_LEVEL 主项生成 CheckItem，状态优先取对应 *_LEVEL
    """
    raw_map: Dict[str, str] = {}
    order: List[str] = []

    for line in (raw or "").splitlines():
        line = line.strip()
        if _should_skip(line):
            continue
        # ERROR: 开头单独处理（记录但不作为检测项）
        if line.startswith("ERROR:"):
            continue
        m = _KV_RE.match(line)
        if not m:
            continue
        key, value = m.group(1).strip(), m.group(2).strip()
        if key not in raw_map:
            order.append(key)
        raw_map[key] = value

    items: List[CheckItem] = []
    for key in order:
        # 跳过 *_LEVEL 辅助字段本身（用于状态判定，不单列为项）
        if key.endswith("_LEVEL"):
            continue
        value = raw_map[key]
        name = DISPLAY_NAMES.get(key, key)
        threshold = THRESHOLDS.get(key, "")
        status = _resolve_status(key, value, raw_map)
        items.append(CheckItem(
            key=key, name=name, value=value,
            threshold=threshold, status=status,
            module_id=module_id, category=category,
        ))
    return items


def _resolve_status(key: str, value: str, raw_map: Dict[str, str]) -> str:
    """判定单项状态，优先级：

    1. 模块自带的 <KEY>_LEVEL 字段（最权威）
    2. 常见 <PREFIX>_LEVEL（如 CPU_USAGE → CPU_LEVEL 之类的关联，此处仅取精确 <KEY>_LEVEL）
    3. 值文本状态词映射
    4. 阈值表数值判定（judge_status）
    """
    # 1. 精确 <KEY>_LEVEL
    level_key = f"{key}_LEVEL"
    if level_key in raw_map:
        lv = raw_map[level_key].strip()
        if lv in (NORMAL, WARNING, CRITICAL):
            return lv

    # 3. 值文本状态词
    stripped = value.strip()
    if stripped in _VALUE_STATUS_MAP:
        return _VALUE_STATUS_MAP[stripped]

    # 4. 阈值表判定（数值/百分比）
    threshold = THRESHOLDS.get(key, "")
    if threshold:
        judged = judge_status(key, value, threshold)
        if judged:
            return judged

    return NORMAL


def summarize(items: List[CheckItem]) -> Dict[str, int]:
    """汇总各状态计数。"""
    summary = {NORMAL: 0, WARNING: 0, CRITICAL: 0, "total": len(items)}
    for it in items:
        if it.status in summary:
            summary[it.status] += 1
    return summary
