"""G-code 补丁配置解析与应用。

本模块提供了从 INI 配置文件读取 G-code 修改规则的功能，
支持两种配置节格式：新版 [patch.<名称>] 节和旧版 [Gcode] 节。
补丁规则可用于在打包前对换板 G-code 进行查找替换等文本修改。"""

from __future__ import annotations

import configparser
from pathlib import Path

from .gcode import normalize_newlines
from .models import DEFAULT_INSERT_BEFORE_MARKER, GcodePatchConfig, GcodePatchRule
from .paths import default_patch_config_path


def parse_bool(value: str | None, default: bool = True) -> bool:
    """将 INI 配置中的字符串值解析为布尔值。

    支持的值: "1", "true", "yes", "on", "enabled"（不区分大小写）。

    参数:
        value:   配置字符串值，可为 None。
        default: value 为 None 时的默认返回值。

    返回:
        解析后的布尔值。
    """
    if value is None:
        return default
    return value.strip().lower() in {"1", "true", "yes", "on", "enabled"}


def parse_patch_config(path: Path | None = None) -> GcodePatchConfig:
    """从 INI 配置文件解析 G-code 补丁配置。

    支持两种配置格式:
    1. 新版 [patch.<规则名>] 节，每个节定义一组查找/替换规则。
    2. 旧版 [Gcode] 节，通过 Postion<键> / Postion<键>_edit / Postion<键>_flag
       三联键定义规则，FinishFlag 定义插入位置标记。

    参数:
        path: 配置文件路径，为 None 时使用默认路径。

    返回:
        包含所有补丁规则和插入标记的 GcodePatchConfig 实例。
    """
    config_path = path or default_patch_config_path()
    if not config_path.exists():
        return GcodePatchConfig(rules=())

    # configparser 配置: 禁用插值，保持键名大小写
    parser = configparser.ConfigParser(interpolation=None, strict=False)
    parser.optionxform = str
    parser.read(config_path, encoding="utf-8-sig")

    # 读取 [swap] 节中的插入标记配置
    insert_before_marker = DEFAULT_INSERT_BEFORE_MARKER
    if parser.has_section("swap"):
        insert_before_marker = parser.get("swap", "insert_before_marker", fallback=insert_before_marker).strip().strip('"')

    rules: list[GcodePatchRule] = []

    # 解析新版 [patch.<名称>] 格式的补丁规则
    for section in parser.sections():
        if section.lower().startswith("patch."):
            enabled = parse_bool(parser.get(section, "enabled", fallback="true"), True)
            find = parser.get(section, "find", fallback="").strip().strip('"')
            replace = parser.get(section, "replace", fallback="").strip().strip('"')
            flag = parser.get(section, "flag", fallback="").strip().strip('"')
            max_count_text = parser.get(section, "max_count", fallback="1").strip()
            try:
                max_count = max(0, int(max_count_text))
            except ValueError:
                max_count = 1
            # 仅当 find 非空且规则启用时才添加
            if find and enabled:
                rules.append(GcodePatchRule(section.split(".", 1)[1], find, replace, flag, enabled, max_count))

    # 解析旧版 [Gcode] 格式的补丁规则（兼容旧配置文件）
    if parser.has_section("Gcode"):
        gcode = parser["Gcode"]
        # 找出所有 Postion<键>_flag 键，提取基准键名
        base_names = sorted(
            key[:-5]
            for key in gcode.keys()
            if key.startswith("Postion") and key.endswith("_flag")
        )
        for base in base_names:
            find = gcode.get(base, "").strip().strip('"')
            replace = gcode.get(f"{base}_edit", "").strip().strip('"')
            flag = gcode.get(f"{base}_flag", "").strip().strip('"')
            if find and replace:
                rules.append(GcodePatchRule(base, find, replace, flag, True, 1))
        # FinishFlag 作为插入位置标记
        marker = gcode.get("FinishFlag", "").strip().strip('"')
        if marker:
            insert_before_marker = marker

    return GcodePatchConfig(tuple(rules), insert_before_marker)


def apply_patch_rule(text: str, rule: GcodePatchRule) -> str:
    """对 G-code 文本应用单条补丁规则。

    查找替换逻辑:
    - 如果规则未启用、查找文本为空或 max_count 为 0，则原样返回。
    - 如果指定了 flag 标记行，则只在该标记行之后开始查找替换。
    - 每次替换后计数器递增，达到 max_count 后停止。

    参数:
        text: 待处理的 G-code 文本。
        rule: 要应用的补丁规则。

    返回:
        应用规则后的 G-code 文本。
    """
    if not rule.enabled or not rule.find or rule.max_count == 0:
        return text

    lines = normalize_newlines(text).split("\n")
    start_index = 0

    # 如果指定了 flag 标记，找到标记行后从下一行开始查找替换
    if rule.flag:
        for index, line in enumerate(lines):
            if line.strip() == rule.flag or rule.flag in line:
                start_index = index + 1
                break

    replacements = 0
    # 在指定范围内查找并替换匹配行
    for index in range(start_index, len(lines)):
        if lines[index].strip() == rule.find or lines[index] == rule.find:
            lines[index] = rule.replace
            replacements += 1
            if replacements >= rule.max_count:
                break

    return "\n".join(lines)


def apply_gcode_patches(text: str, config: GcodePatchConfig) -> str:
    """对 G-code 文本依次应用配置中的所有补丁规则。

    参数:
        text:   待处理的 G-code 文本。
        config: 包含补丁规则列表的配置对象。

    返回:
        应用所有规则后的 G-code 文本。
    """
    patched = normalize_newlines(text)
    for rule in config.rules:
        patched = apply_patch_rule(patched, rule)
    return patched
