"""CLI 入口：click 命令组，默认命令、init、config 子命令。"""

from __future__ import annotations

import sys
from pathlib import Path

import click

from acm import __version__
from acm.config import (
    ACMConfig,
    CommitConfig,
    ConfigError,
    GLOBAL_CONFIG_DIR,
    GLOBAL_CONFIG_FILE,
    LLMConfig,
    PROJECT_CONFIG_FILE,
    PROVIDER_DEFAULTS,
    config_get,
    config_set,
    load_config,
    save_config,
    validate_config,
)
from acm.feedback import add_feedback, clear_feedback, load_feedback
from acm.generator import CommitGenerator, GenerateError
from acm.git import (
    DiffResult,
    GitError,
    add_all,
    build_diff_result,
    commit,
    commit_files,
    get_staged_files,
    has_unstaged_changes,
    has_untracked_files,
    is_git_repo,
)
from acm.llm import create_llm
from acm.splitter import CommitSplitter, SplitError
from acm.ui import (
    console,
    print_error,
    print_info,
    print_success,
    print_verbose,
    print_warning,
    prompt_action,
    prompt_confirm,
    prompt_edit,
    prompt_feedback,
    show_commit_message,
    show_diff_summary,
    show_split_suggestion,
    spinner,
    stream_commit_message,
    warn_large_commit,
)


LARGE_COMMIT_THRESHOLD = 20


@click.group(invoke_without_command=True)
@click.option("--dry-run", is_flag=True, help="仅生成 message，不执行 git commit")
@click.option("--verbose", is_flag=True, help="输出详细调试信息")
@click.option("--split", is_flag=True, help="智能拆分为多个原子提交")
@click.version_option(__version__, prog_name="acm")
@click.pass_context
def main(ctx: click.Context, dry_run: bool, verbose: bool, split: bool) -> None:
    """ACM - 基于 LLM 的 Git Commit Message 自动生成工具。"""
    ctx.ensure_object(dict)
    ctx.obj["verbose"] = verbose

    if ctx.invoked_subcommand is not None:
        return

    # 主流程：生成 commit message
    _run_generate(dry_run=dry_run, verbose=verbose, split_mode=split)


def _run_generate(dry_run: bool, verbose: bool, split_mode: bool) -> None:
    """主流程：分析 staged 内容，生成 commit message。"""
    # 1. 检查 Git 仓库
    if not is_git_repo():
        print_error("当前目录不是 Git 仓库，请在 Git 项目根目录下运行")
        sys.exit(1)

    # 2. 检查 staged 文件
    try:
        staged_files = get_staged_files()
    except GitError as e:
        print_error(str(e))
        sys.exit(1)

    if not staged_files:
        has_unstaged = has_unstaged_changes()
        has_untracked = has_untracked_files()
        if has_unstaged or has_untracked:
            if has_unstaged and has_untracked:
                print_warning("暂存区为空，但检测到未暂存的修改和未跟踪的新文件")
            elif has_unstaged:
                print_warning("暂存区为空，但检测到未暂存的修改")
            else:
                print_warning("暂存区为空，但检测到未跟踪的新文件")

            if prompt_confirm("是否执行 git add . 将所有变更添加到暂存区并继续？"):
                try:
                    add_all()
                    print_success("已执行 git add -A，所有变更已添加到暂存区")
                    # 重新获取 staged 文件并继续流程
                    staged_files = get_staged_files()
                    if not staged_files:
                        print_error("执行 git add 后暂存区仍为空")
                        sys.exit(1)
                except GitError as e:
                    print_error(f"执行 git add 失败: {e}")
                    sys.exit(1)
            else:
                print_info("已取消，请手动执行 git add 后再重试")
                sys.exit(0)
        else:
            print_error("暂存区为空，没有需要提交的变更")
            sys.exit(1)

    # 3. 大提交警告
    if len(staged_files) > LARGE_COMMIT_THRESHOLD:
        if not warn_large_commit(len(staged_files)):
            sys.exit(0)

    # 4. 加载配置
    try:
        config = load_config()
        validate_config(config)
    except ConfigError as e:
        print_error(str(e))
        sys.exit(1)

    if verbose:
        _show_verbose_config(config)

    # 5. 获取 diff
    try:
        diff_result = build_diff_result(
            max_diff_lines=config.commit.max_diff_lines,
            max_diff_chars=config.commit.max_diff_chars,
        )
    except GitError as e:
        print_error(str(e))
        sys.exit(1)

    show_diff_summary(diff_result)

    # 6. 创建 LLM 实例
    try:
        llm = create_llm(config)
    except Exception as e:
        print_error(f"创建 LLM 实例失败: {e}")
        sys.exit(1)

    # 7. 智能拆分模式
    if split_mode:
        _run_split_mode(llm, config, diff_result, staged_files, dry_run, verbose)
        return

    # 8. 常规生成模式（流式输出）
    generator = CommitGenerator(llm, config)

    try:
        stream = generator.stream_generate(diff_result.diff_content)
        message = stream_commit_message(stream)
    except (GenerateError, Exception) as e:
        print_error(f"LLM 接口调用失败: {e}")
        if verbose:
            import traceback
            print_verbose(traceback.format_exc())
        sys.exit(1)

    if verbose:
        print_verbose(f"LLM 原始响应: {message}")

    show_commit_message(message)

    if dry_run:
        print_info("(dry-run 模式，不执行 git commit)")
        return

    # 9. 交互循环
    _interaction_loop(generator, diff_result, message, verbose)


def _interaction_loop(
    generator: CommitGenerator,
    diff_result: DiffResult,
    message: str,
    verbose: bool,
) -> None:
    """用户交互循环：确认/编辑/反馈/放弃。"""
    while True:
        action = prompt_action()

        if action == "y":
            _do_commit(message)
            return

        elif action == "e":
            previous_message = message
            message = prompt_edit(message)
            # 记录编辑偏好（仅当用户实际修改了 message 时）
            if message != previous_message:
                try:
                    add_feedback(previous_message, "用户手动编辑", message)
                except Exception:
                    pass
            show_commit_message(message)
            continue

        elif action == "r":
            feedback = prompt_feedback()
            if not feedback:
                print_warning("未输入反馈内容，请重新选择操作")
                show_commit_message(message)
                continue

            previous_message = message
            try:
                stream = generator.stream_regenerate(feedback)
                message = stream_commit_message(stream)
            except (GenerateError, Exception) as e:
                print_error(f"重新生成失败: {e}")
                if verbose:
                    import traceback
                    print_verbose(traceback.format_exc())
                continue

            # 记录反馈到本地，用于后续 prompt 优化
            try:
                add_feedback(previous_message, feedback, message)
            except Exception:
                pass  # 反馈记录失败不影响主流程

            show_commit_message(message)
            continue

        elif action == "q":
            print_info("已放弃提交")
            return


def _do_commit(message: str) -> None:
    """执行 git commit。"""
    try:
        output = commit(message)
        print_success("提交成功！")
        console.print(f"[dim]{output.strip()}[/dim]")
    except GitError as e:
        print_error(f"提交失败: {e}")


def _run_split_mode(
    llm,
    config: ACMConfig,
    diff_result: DiffResult,
    staged_files,
    dry_run: bool,
    verbose: bool,
) -> None:
    """智能拆分模式。"""
    splitter = CommitSplitter(llm, config)
    file_paths = [f.path for f in staged_files]

    with spinner("分析变更，生成拆分建议..."):
        try:
            groups = splitter.split(diff_result.diff_content, file_paths)
        except (SplitError, Exception) as e:
            print_error(f"拆分分析失败: {e}")
            sys.exit(1)

    show_split_suggestion(groups)

    if dry_run:
        print_info("(dry-run 模式，不执行 git commit)")
        return

    if not prompt_confirm("是否按上述方案执行拆分提交？"):
        print_info("已取消拆分提交")
        return

    for i, group in enumerate(groups, 1):
        try:
            output = commit_files(group.files, group.message)
            print_success(f"提交 {i}/{len(groups)} 完成: {group.message}")
        except GitError as e:
            print_error(f"提交 {i} 失败: {e}")
            return


def _show_verbose_config(config: ACMConfig) -> None:
    """verbose 模式下显示配置信息。"""
    print_verbose("--- 当前配置 ---")
    print_verbose(f"提供商: {config.llm.provider}")
    print_verbose(f"模型: {config.llm.model}")
    print_verbose(f"Base URL: {config.llm.base_url}")
    print_verbose(f"API Key: {'*' * 8}...{config.llm.api_key[-4:]}" if len(config.llm.api_key) > 4 else "API Key: ***")
    print_verbose(f"语言: {config.commit.language}")
    print_verbose(f"Diff 截断阈值: {config.commit.max_diff_lines} 行 / {config.commit.max_diff_chars} 字符")
    print_verbose("---")
    console.print()


# ---------------------------------------------------------------------------
# init 子命令
# ---------------------------------------------------------------------------

@main.command()
@click.option("--global", "is_global", is_flag=True, default=False, help="生成全局配置文件")
def init(is_global: bool) -> None:
    """交互式初始化配置。"""
    console.print("[bold]ACM 配置初始化[/bold]")
    console.print()

    # 1. 选择提供商
    providers = list(PROVIDER_DEFAULTS.keys()) + ["other"]
    console.print("可选提供商：")
    for i, p in enumerate(providers, 1):
        console.print(f"  {i}. {p}")

    provider_idx = click.prompt("选择提供商", type=int, default=1) - 1
    if provider_idx < 0 or provider_idx >= len(providers):
        provider_idx = 0

    provider = providers[provider_idx]
    if provider == "other":
        provider = click.prompt("输入提供商名称", type=str)

    # 2. API Key
    api_key = click.prompt("输入 API Key", type=str, hide_input=True)

    # 3. 模型名称
    model = click.prompt("输入模型名称 (必填)", type=str)
    if not model:
        print_error("模型名称不能为空")
        sys.exit(1)

    # 4. Base URL（可选）
    default_url = PROVIDER_DEFAULTS.get(provider, {}).get("base_url", "")
    if default_url:
        console.print(f"[dim]默认 Base URL: {default_url}[/dim]")
    base_url = click.prompt("自定义 Base URL (留空使用默认)", type=str, default="")

    # 5. 语言
    language = click.prompt("输出语言", type=click.Choice(["zh-CN", "en"]), default="zh-CN")

    # 构建配置
    config = ACMConfig(
        llm=LLMConfig(provider=provider, api_key=api_key, model=model, base_url=base_url),
        commit=CommitConfig(language=language),
    )

    # 保存
    if is_global:
        path = GLOBAL_CONFIG_FILE
    else:
        path = Path.cwd() / PROJECT_CONFIG_FILE

    save_config(config, path)
    print_success(f"配置已保存到: {path}")


# ---------------------------------------------------------------------------
# config 子命令
# ---------------------------------------------------------------------------

@main.group()
def config() -> None:
    """查看和修改配置项。"""
    pass


@config.command("list")
def config_list() -> None:
    """列出当前生效的所有配置。"""
    cfg = load_config()
    console.print("[bold]当前生效配置:[/bold]")
    console.print()

    items = [
        ("llm.provider", cfg.llm.provider),
        ("llm.model", cfg.llm.model),
        ("llm.base_url", cfg.llm.base_url),
        ("llm.api_key", f"{'*' * 8}...{cfg.llm.api_key[-4:]}" if len(cfg.llm.api_key) > 4 else "***"),
        ("commit.language", cfg.commit.language),
        ("commit.max_diff_lines", str(cfg.commit.max_diff_lines)),
        ("commit.max_diff_chars", str(cfg.commit.max_diff_chars)),
        ("commit.types", ", ".join(cfg.commit.types) if cfg.commit.types else "(默认)"),
        ("prompt.system", "(自定义)" if cfg.prompt.system else "(默认)"),
        ("prompt.extra", cfg.prompt.extra or "(无)"),
    ]

    for key, value in items:
        console.print(f"  [cyan]{key}[/cyan] = {value}")


@config.command("get")
@click.argument("key")
def config_get_cmd(key: str) -> None:
    """查看某个配置项的值。"""
    try:
        cfg = load_config()
        value = config_get(key, cfg)
        console.print(value)
    except ConfigError as e:
        print_error(str(e))
        sys.exit(1)


@config.command("set")
@click.argument("key")
@click.argument("value")
@click.option("--global", "is_global", is_flag=True, default=False, help="修改全局配置文件")
def config_set_cmd(key: str, value: str, is_global: bool) -> None:
    """修改某个配置项的值。"""
    if is_global:
        path = GLOBAL_CONFIG_FILE
    else:
        path = Path.cwd() / PROJECT_CONFIG_FILE

    try:
        config_set(key, value, path)
        print_success(f"已设置 {key} = {value} (文件: {path})")
    except ConfigError as e:
        print_error(str(e))
        sys.exit(1)


# ---------------------------------------------------------------------------
# feedback 子命令
# ---------------------------------------------------------------------------

@main.group()
def feedback() -> None:
    """管理反馈学习记录。"""
    pass


@feedback.command("list")
def feedback_list() -> None:
    """列出所有历史反馈记录。"""
    records = load_feedback()
    if not records:
        print_info("暂无反馈记录")
        return

    console.print(f"[bold]共 {len(records)} 条反馈记录:[/bold]")
    console.print()
    for i, r in enumerate(records, 1):
        console.print(f"  [cyan]#{i}[/cyan]")
        console.print(f"    原始: [dim]{r.original_message}[/dim]")
        console.print(f"    反馈: [yellow]{r.feedback}[/yellow]")
        console.print(f"    改进: [green]{r.improved_message}[/green]")
        console.print()


@feedback.command("clear")
def feedback_clear() -> None:
    """清空所有反馈记录。"""
    count = clear_feedback()
    if count > 0:
        print_success(f"已清除 {count} 条反馈记录")
    else:
        print_info("暂无反馈记录需要清除")


if __name__ == "__main__":
    main()
