"""智能拆分提交：LLM 分析 staged 内容，按逻辑分组为多个原子提交。"""

from __future__ import annotations

import json
from dataclasses import dataclass

from acm.config import ACMConfig
from acm.llm.base import LLMBase
from acm.prompt import build_split_system_prompt, build_split_user_prompt


class SplitError(Exception):
    """拆分提交相关错误。"""


@dataclass
class CommitGroup:
    """一个原子提交组。"""
    files: list[str]
    message: str


def _parse_split_response(raw: str) -> list[CommitGroup]:
    """解析 LLM 返回的 JSON 拆分结果。"""
    text = raw.strip()
    # 尝试提取 JSON 块
    if "```" in text:
        # 从 markdown 代码块中提取
        start = text.find("[")
        end = text.rfind("]")
        if start != -1 and end != -1:
            text = text[start:end + 1]

    try:
        data = json.loads(text)
    except json.JSONDecodeError as e:
        raise SplitError(f"无法解析 LLM 返回的拆分结果: {e}")

    if not isinstance(data, list):
        raise SplitError("LLM 返回的拆分结果格式错误，应为 JSON 数组")

    groups = []
    for item in data:
        if not isinstance(item, dict):
            raise SplitError("拆分结果中的每个元素应为对象")
        files = item.get("files", [])
        message = item.get("message", "")
        if not files or not message:
            raise SplitError("拆分结果中的每个元素必须包含 files 和 message")
        groups.append(CommitGroup(files=files, message=message))

    return groups


class CommitSplitter:
    """智能拆分提交。"""

    def __init__(self, llm: LLMBase, config: ACMConfig):
        self._llm = llm
        self._config = config

    def split(self, diff_content: str, files: list[str]) -> list[CommitGroup]:
        """分析 diff 内容并返回拆分建议。

        Args:
            diff_content: git diff 内容。
            files: staged 文件路径列表。

        Returns:
            拆分后的提交组列表。
        """
        system_prompt = build_split_system_prompt(self._config)
        user_prompt = build_split_user_prompt(diff_content, files)

        raw = self._llm.chat(system_prompt, user_prompt)
        if not raw:
            raise SplitError("LLM 返回了空内容")

        groups = _parse_split_response(raw)

        # 校验：所有文件都应被分配到某个组
        assigned = set()
        for group in groups:
            assigned.update(group.files)

        missing = set(files) - assigned
        if missing:
            # 将未分配的文件归入最后一组
            groups[-1].files.extend(sorted(missing))

        return groups
