import subprocess

import sqlparse

CLANG_FORMAT_STYLE = "{BasedOnStyle: LLVM, IndentWidth: 4, BreakBeforeBraces: Attach}"


class FormatSyntaxError(Exception):
    """格式化工具因代码本身存在语法错误而失败（目前只有 ruff format 会触发）"""


class FormatToolError(Exception):
    """格式化工具执行本身失败：超时、二进制缺失、非预期的非零退出码"""


def format_code(code, language):
    """
    :param code: 用户代码
    :param language: "python" | "c" | "cpp" | "sql"
    :return: 格式化后的代码字符串
    :raises FormatSyntaxError: 代码语法错误导致格式化失败（仅 python）
    :raises FormatToolError: 格式化工具本身执行失败
    """
    if language == "python":
        return _format_with_ruff(code)
    if language == "sql":
        return _format_with_sql(code)
    return _format_with_clang(code, language)


def _format_with_sql(code):
    # sqlparse 对语法错误宽容，不会抛异常，语法问题留给判题阶段反馈
    # strip_whitespace 会把多条语句压成一行，先按分号拆分再逐条格式化
    statements = sqlparse.split(code)
    return "\n\n".join(sqlparse.format(s, strip_whitespace=True, keyword_case="upper") for s in statements)


def _format_with_ruff(code):
    try:
        result = subprocess.run(
            ["ruff", "format", "--stdin-filename", "code.py", "-"],
            input=code,
            capture_output=True,
            text=True,
            timeout=5,
        )
    except (subprocess.TimeoutExpired, OSError) as e:
        raise FormatToolError(str(e))
    if result.returncode != 0:
        raise FormatSyntaxError(result.stderr)
    return result.stdout


def _format_with_clang(code, language):
    filename = "code.c" if language == "c" else "code.cpp"
    try:
        result = subprocess.run(
            [
                "clang-format",
                f"-assume-filename={filename}",
                f"-style={CLANG_FORMAT_STYLE}",
            ],
            input=code,
            capture_output=True,
            text=True,
            timeout=5,
        )
    except (subprocess.TimeoutExpired, OSError) as e:
        raise FormatToolError(str(e))
    if result.returncode != 0:
        raise FormatToolError(result.stderr)
    return result.stdout
