import subprocess
import os
import pytest
import tempfile
import shutil
from pathlib import Path
from typing import List

SCRIPTS_DIR = Path(__file__).parent.parent.parent / "kadt" / "llm_service"

SCRIPTS: List[str] = [
    "install_base_sw.sh",
    "install_llm.sh",
    "download.sh",
]


def run_script(args: List[str], cwd: Path = None) -> subprocess.CompletedProcess:
    return subprocess.run(
        args,
        stdout=subprocess.PIPE,
        stderr=subprocess.PIPE,
        text=True,
        cwd=cwd
    )


def has_help_message(output: str) -> bool:
    keywords = ["帮助", "help", "Help", "Usage", "usage", "Options"]
    return any(kw in output for kw in keywords)


def has_error_message(output: str) -> bool:
    keywords = ["错误", "error", "Error", "失败", "failed", "invalid", "Invalid"]
    return any(kw in output for kw in keywords)


class TestScriptExistence:
    @pytest.mark.parametrize("script", SCRIPTS)
    @staticmethod
    def test_script_exists(script: str) -> None:
        path = SCRIPTS_DIR / script
        assert path.is_file(), f"{script} 不存在于 {SCRIPTS_DIR}"


class TestScriptHelp:
    @pytest.mark.parametrize("script", SCRIPTS)
    @staticmethod
    def test_help_message(script: str) -> None:
        path = SCRIPTS_DIR / script
        result = run_script(["bash", str(path), "--help"], cwd=SCRIPTS_DIR)
        assert result.returncode in (0, 1), f"{script} --help 返回异常退出码: {result.returncode}"
        output = result.stdout + result.stderr
        assert has_help_message(output), f"{script} 没有输出帮助信息, 输出: {output[:200]}"


class TestVllmDownload:
    @staticmethod
    def test_no_args_shows_help() -> None:
        script_path = SCRIPTS_DIR / "download.sh"
        result = run_script(["bash", str(script_path)], cwd=SCRIPTS_DIR)
        output = result.stdout + result.stderr
        assert result.returncode != 0, "无参数调用应返回非零退出码"
        assert has_help_message(output), f"无参数时应显示帮助信息, 输出: {output[:200]}"

    @staticmethod
    def test_invalid_os_param_rejected() -> None:
        script_path = SCRIPTS_DIR / "download.sh"
        result = run_script(
            ["bash", str(script_path), "--os", "not_exist_os", "-o", "openEuler", "-m", "1"],
            cwd=SCRIPTS_DIR
        )
        output = result.stdout + result.stderr
        assert result.returncode != 127, "命令不应找不到 (exit 127)"
        assert result.returncode != 0 or has_help_message(output) or has_error_message(output), \
            f"无效 OS 参数应被拒绝或提示错误, 返回码: {result.returncode}, 输出: {output[:200]}"


class TestInstallBaseSw:
    @staticmethod
    def test_no_args_runs_or_shows_info() -> None:
        script_path = SCRIPTS_DIR / "install_base_sw.sh"
        result = run_script(["bash", str(script_path)], cwd=SCRIPTS_DIR)
        output = result.stdout + result.stderr
        assert "INSTALL_SOFTWARE" in output or has_help_message(output) or result.returncode != 0, \
            f"无参数调用应显示配置信息或帮助, 输出: {output[:200]}"

    @staticmethod
    def test_invalid_software_param_rejected() -> None:
        script_path = SCRIPTS_DIR / "install_base_sw.sh"
        result = run_script(
            ["bash", str(script_path), "--install-software", "not_exist_pkg"],
            cwd=SCRIPTS_DIR
        )
        output = result.stdout + result.stderr
        assert result.returncode != 0 or has_error_message(output), \
            f"无效软件包参数应被拒绝, 返回码: {result.returncode}, 输出: {output[:200]}"


class TestInstallLlm:
    @staticmethod
    def test_no_args_shows_help() -> None:
        script_path = SCRIPTS_DIR / "install_llm.sh"
        result = run_script(["bash", str(script_path)], cwd=SCRIPTS_DIR)
        output = result.stdout + result.stderr
        assert has_help_message(output), f"无参数时应显示帮助信息, 输出: {output[:200]}"

    @staticmethod
    def test_invalid_model_param_rejected() -> None:
        script_path = SCRIPTS_DIR / "install_llm.sh"
        result = run_script([
            "bash", str(script_path),
            "--model", "not_exist_model",
            "--port", "8000",
            "--api-key", "test"
        ], cwd=SCRIPTS_DIR)
        output = result.stdout + result.stderr
        assert result.returncode != 0 or has_error_message(output), \
            f"无效模型参数应被拒绝, 返回码: {result.returncode}, 输出: {output[:200]}"


class TestMissingCommonScript:
    @staticmethod
    def test_missing_common_directory() -> None:
        with tempfile.TemporaryDirectory() as tmpdir:
            for script in SCRIPTS:
                src = SCRIPTS_DIR / script
                dst = Path(tmpdir) / script
                shutil.copy(src, dst)

            for script in SCRIPTS:
                test_script = Path(tmpdir) / script
                result = run_script(["bash", str(test_script), "--help"], cwd=Path(tmpdir))
                output = result.stdout + result.stderr
                assert "未找到公共脚本" in output, \
                    f"{script} 缺少 common 时应明确报错'未找到公共脚本', 输出: {output[:200]}"