from __future__ import annotations

import os
import logging
import click
import questionary
from rich.panel import Panel
from rich.prompt import Prompt
from rich import box

from common.config import load_config
from common.input_builder import build_input_context
from common.markdown import clear_last_review_payload, save_last_review_markdown, should_prompt_markdown_save
from common.ui import console, start_progress, update_status
from interface.init_cli import init as init_command

logger = logging.getLogger(__name__)


def _is_config_initialized() -> bool:
    """필수 설정(GAR_LLM_BASE_URL)이 준비되었는지 확인한다."""
    config = load_config()
    llm_base_url = config.get("llm_base_url")
    return bool(llm_base_url and llm_base_url.strip())


def _ensure_initialized(ctx: click.Context) -> bool:
    """필수 설정이 없으면 gar init 실행을 유도한다.

    반환값:
        True  - 설정이 준비되어 있고, 계속 진행해도 됨
        False - 사용자가 init를 거부하거나 중단해서 더 이상 진행하면 안 됨
    """
    if _is_config_initialized():
        return True

    console.print(
        Panel(
            "[bold yellow]GAR 설정이 아직 완료되지 않았습니다.[/bold yellow]\n\n"
            "[white]지금 바로 초기 설정을 진행하시겠습니까?[/white]\n"
            "[dim](GAR_LLM_BASE_URL 등의 값을 .env 에 저장합니다.)[/dim]",
            box=box.DOUBLE,
            border_style="yellow",
        )
    )

    choice = questionary.select(
        "초기 설정을 진행할까요? (↑/↓, Enter):",
        choices=["1. 예, 지금 gar init 실행", "2. 아니요, 그냥 종료"],
        use_shortcuts=True,
    ).ask()

    if choice is None or choice.startswith("2."):
        console.print(Panel("초기 설정 없이 GAR를 실행할 수 없습니다.", box=box.DOUBLE, border_style="red"))
        return False

    # gar init 서브커맨드를 내부에서 호출
    ctx.invoke(init_command)

    # init 이후에도 필수 값이 없으면 더 진행하지 않는다.
    if not _is_config_initialized():
        console.print(
            Panel(
                "초기 설정이 완료되지 않았습니다. .env 내용을 확인한 뒤 다시 시도해주세요.",
                box=box.DOUBLE,
                border_style="red",
            )
        )
        return False

    return True


@click.group(invoke_without_command=True)
@click.version_option(version="0.1.0", prog_name="gar")
# TODO: 나중에 제대로 구현할 verbose 옵션
# @click.option("--verbose", "-v", is_flag=True, help="Enable verbose logging")
@click.option(
    "--mode",
    type=click.Choice(["normal", "interface", "security", "performance", "api_security", "db_security"]),
    help="분석 모드 선택 (interactive 없이 바로 실행)",
)
@click.pass_context
def cli(ctx: click.Context, mode: str | None) -> None:  # verbose: bool 파라미터 제거
    # TODO: 나중에 제대로 구현할 verbose 옵션
    # if verbose:
    #     logging.getLogger().setLevel(logging.DEBUG)
    # else:
    #     logging.getLogger().setLevel(logging.WARNING)
    # 기본적으로 WARNING 레벨로 설정
    logging.getLogger().setLevel(logging.WARNING)

    # 서브커맨드가 없는 경우(예: 그냥 `gar` 또는 `gar --mode ...`)에만 초기 설정을 확인한다.
    if ctx.invoked_subcommand is None:
        if not _ensure_initialized(ctx):
            return

        if mode:
            _run_mode(mode)
        else:
            _interactive_mode_selection()


def _interactive_mode_selection() -> None:
    welcome = """
    🤖 [bold cyan]GAR[/bold cyan] - [bold green]Git Automated Review[/bold green]

    AI 기반 코드 리뷰 도구
    """
    console.print(Panel(welcome, box=box.DOUBLE, border_style="cyan"))

    while True:
        console.print("\n[bold yellow]분석 모드를 선택해주세요 (번호 입력 후 Enter):[/bold yellow]")
        console.print("  [cyan]1.[/cyan] 일반 분석 모드 [dim](코드 품질 및 구조 분석)[/dim]")
        console.print("  [cyan]2.[/cyan] 인터페이스 분석 모드 [dim](인터페이스 효율성 및 품질 분석)[/dim]")
        console.print("  [cyan]3.[/cyan] 보안 분석 모드 [dim](시크릿/취약점 중심 리뷰)[/dim]")
        console.print("  [cyan]4.[/cyan] 성능 분석 모드 [dim](성능 병목 및 비효율 패턴 분석)[/dim]")
        console.print("  [cyan]5.[/cyan] API 보안 분석 모드 [dim](API/인터페이스 관점의 보안 취약점 분석)[/dim]")
        console.print("  [cyan]6.[/cyan] DB 분석 모드 [dim](데이터베이스 구조 및 취약점 분석)[/dim]")
        console.print("")
        console.print("[white]종료:[/white] [bold white]q/quit[/bold white] 입력 후 Enter 또는 Ctrl+C")

        while True:
            try:
                raw_choice = Prompt.ask(
                    "\n[bold green]선택[/bold green]",
                    default="1",
                    show_default=True,
                )
            except KeyboardInterrupt:
                console.print()
                console.print(Panel("리뷰를 종료합니다.", box=box.DOUBLE, border_style="cyan"))
                return
            
            choice = raw_choice.strip()
            
            if choice.lower() == "q" or choice.lower() == "quit":
                console.print()
                console.print(Panel("리뷰를 종료합니다.", box=box.DOUBLE, border_style="cyan"))
                return
            
            if choice in {"1", "2", "3", "4", "5", "6"}:
                break
            
            if choice.lower() in {"normal", "interface", "security", "performance", "api_security", "db_security"}:
                choice = {
                    "normal": "1",
                    "interface": "2",
                    "security": "3",
                    "performance": "4",
                    "api_security": "5",
                    "db_security": "6",
                }[choice.lower()]
                break
            
            if choice in {"일반", "인터페이스", "보안", "성능", "API 보안", "DB"}:
                choice = {
                    "일반": "1",
                    "인터페이스": "2",
                    "보안": "3",
                    "성능": "4",
                    "API 보안": "5",
                    "DB": "6",
                }[choice]
                break
            
            console.print("[yellow]1/2/3/4/5/6 중에서 선택해주세요.[/yellow]")

        if choice == "1":
            _run_mode("normal")
        elif choice == "2":
            _run_mode("interface")
        elif choice == "3":
            _run_mode("security")
        elif choice == "4":
            _run_mode("performance")
        elif choice == "5":
            _run_mode("api_security")
        elif choice == "6":
            _run_mode("db_security")

        if should_prompt_markdown_save() and _ask_markdown_save_interactively():
            saved_path = save_last_review_markdown()
            if saved_path:
                console.print(Panel(f"저장 완료: {saved_path}", box=box.DOUBLE, border_style="cyan"))
            else:
                console.print(Panel("저장할 결과가 없습니다.", box=box.DOUBLE, border_style="cyan"))


def _ask_interface_scope() -> str:
    console.print("\n[bold yellow]인터페이스 분석 범위를 선택해주세요:[/bold yellow]")
    console.print("  [cyan]1.[/cyan] staged 변경 파일만")
    console.print("  [cyan]2.[/cyan] 전체 코드 (레포 기준)")
    while True:
        raw_choice = Prompt.ask(
            "\n[bold green]선택[/bold green]",
            default="1",
            show_default=True,
        )
        choice = raw_choice.strip()
        if choice in {"1", "2"}:
            return "staged" if choice == "1" else "full"
        console.print("[yellow]1/2 중에서 선택해주세요.[/yellow]")


def _ask_normal_scope() -> str:
    """일반 분석 모드에서 리뷰 범위를 선택하는 함수."""
    console.print("\n[bold yellow]일반 분석 범위를 선택해주세요:[/bold yellow]")
    console.print("  [cyan]1.[/cyan] staged 변경 파일만")
    console.print("  [cyan]2.[/cyan] 전체 코드 (레포 기준)")
    while True:
        raw_choice = Prompt.ask(
            "\n[bold green]선택[/bold green]",
            default="1",
            show_default=True,
        )
        choice = raw_choice.strip()
        if choice in {"1", "2"}:
            return "staged" if choice == "1" else "all"
        console.print("[yellow]1/2 중에서 선택해주세요.[/yellow]")


def _ask_chunking_mode() -> str:
    """청킹 전략을 선택하는 함수."""
    console.print("\n[bold yellow]청킹 전략을 선택해주세요:[/bold yellow]")
    console.print("  [cyan]1.[/cyan] Fast    - 여러 파일 단위 분석")
    console.print("  [cyan]2.[/cyan] Thinking - 파일 단위 분석")
    while True:
        try:
            raw_choice = Prompt.ask(
                "\n[bold green]선택[/bold green]",
                default="1",
                show_default=True,
            )
            if raw_choice is None:
                raise KeyboardInterrupt("청킹 전략 선택이 취소되었습니다.")
            choice = raw_choice.strip()
            if choice in {"1", "2"}:
                return "fast" if choice == "1" else "thinking"
            console.print("[yellow]1/2 중에서 선택해주세요.[/yellow]")
        except KeyboardInterrupt:
            raise

def _ask_security_scope() -> str:
    """보안 분석 모드에서 리뷰 범위를 선택하는 함수."""
    console.print("\n[bold yellow]보안 분석 범위를 선택해주세요:[/bold yellow]")
    console.print("  [cyan]1.[/cyan] staged 변경 파일만")
    console.print("  [cyan]2.[/cyan] 전체 코드 (레포 기준)")
    while True:
        raw_choice = Prompt.ask(
            "\n[bold green]선택[/bold green]",
            default="1",
            show_default=True,
        )
        choice = raw_choice.strip()
        if choice in {"1", "2"}:
            return "staged" if choice == "1" else "all"
        console.print("[yellow]1/2 중에서 선택해주세요.[/yellow]")


def _ask_api_security_scope() -> str:
    """API 보안 분석 모드에서 리뷰 범위를 선택하는 함수."""
    console.print("\n[bold yellow]API 보안 분석 범위를 선택해주세요:[/bold yellow]")
    console.print("  [cyan]1.[/cyan] staged 변경 파일만")
    console.print("  [cyan]2.[/cyan] 전체 코드 (레포 기준)")
    while True:
        raw_choice = Prompt.ask(
            "\n[bold green]선택[/bold green]",
            default="1",
            show_default=True,
        )
        choice = raw_choice.strip()
        if choice in {"1", "2"}:
            return "staged" if choice == "1" else "all"
        console.print("[yellow]1/2 중에서 선택해주세요.[/yellow]")


def _ask_performance_scope() -> str:
    """성능 분석 모드에서 리뷰 범위를 선택하는 함수."""
    console.print("\n[bold yellow]성능 분석 범위를 선택해주세요:[/bold yellow]")
    console.print("  [cyan]1.[/cyan] staged 변경 파일만")
    console.print("  [cyan]2.[/cyan] 전체 코드 (레포 기준)")
    while True:
        raw_choice = Prompt.ask(
            "\n[bold green]선택[/bold green]",
            default="1",
            show_default=True,
        )
        choice = raw_choice.strip()
        if choice in {"1", "2"}:
            return "staged" if choice == "1" else "all"
        console.print("[yellow]1/2 중에서 선택해주세요.[/yellow]")

def _print_debug(input_context: dict) -> None:
    if os.getenv("GAR_DEBUG") != "1":
        return
    truncated_count = sum(1 for f in input_context.get("files", []) if f.get("truncated"))
    console.print("[dim]GAR_DEBUG=1[/dim]")
    console.print(f"[dim]- scope: {input_context.get('scope')}[/dim]")
    console.print(f"[dim]- selected_paths: {len(input_context.get('selected_paths', []))}개[/dim]")
    console.print(f"[dim]- files: {len(input_context.get('files', []))}개[/dim]")
    console.print(f"[dim]- truncated: {truncated_count}개[/dim]")


def _run_mode(mode: str) -> int:
    clear_last_review_payload()
    # 먼저 모든 선택을 받음 (progress 시작 전)
    if mode == "interface":
        scope = _ask_interface_scope()
    elif mode == "normal":
        scope = _ask_normal_scope()
    elif mode == "security":
        scope = _ask_security_scope()
    elif mode == "performance":
        scope = _ask_performance_scope()
    elif mode == "api_security":
        scope = _ask_api_security_scope()
    elif mode == "db_security":
        # DB 보안 모드는 일반 모드와 동일하게 staged / all 지원
        scope = _ask_normal_scope()
    else:
        scope = "staged"

    # Normal 모드의 경우 청킹 모드도 선택
    # Security/API Security 모드에서 scope가 "all"이면 자동으로 "fast" 모드 사용
    chunking_mode = None
    if mode == "normal":
        try:
            chunking_mode = _ask_chunking_mode()
        except KeyboardInterrupt:
            console.print("\n[yellow]취소되었습니다.[/yellow]")
            raise SystemExit(0)
    elif mode in {"security", "api_security"} and scope == "all":
        chunking_mode = "fast"

    # 모든 선택이 완료된 후 progress 시작
    mode_labels = {
        "normal": "일반 분석 모드",
        "interface": "인터페이스 분석 모드",
        "security": "보안 분석 모드",
        "performance": "성능 분석 모드",
        "api_security": "API 보안 분석 모드",
        "db_security": "DB 분석 모드",
    }
    # 임시로 handle 생성 (파일 개수는 build_input_context에서 결정)
    handle = start_progress(mode_labels.get(mode, mode), total_files=0)
    
    input_context = build_input_context(
        mode=mode,
        scope=scope,
        status_hook=lambda text: update_status(handle, text),
        progress_handle=handle,  # 파일 읽기 진행률 추적용
    )
    input_context["_ui_handle"] = handle
    if chunking_mode:
        input_context["chunking_mode"] = chunking_mode
    _print_debug(input_context)

    if mode == "normal":
        from normal.runner import run as run_normal
        exit_code = _call_runner(run_normal, input_context)
    elif mode == "security":
        from security.runner import run as run_security
        exit_code = _call_runner(run_security, input_context)
    elif mode == "performance":
        from performance.runner import run as run_performance
        exit_code = _call_runner(run_performance, input_context)
    elif mode == "api_security":
        from api_security.runner import run as run_api_security
        exit_code = _call_runner(run_api_security, input_context)
    elif mode == "db_security":
        from db_security.runner import run as run_db_security
        exit_code = _call_runner(run_db_security, input_context)
    else:
        from interface.runner import run as run_interface
        exit_code = _call_runner(run_interface, input_context)

    return exit_code


def _ask_markdown_save_interactively() -> bool:
    choice = questionary.select(
        "분석 결과를 마크다운으로 저장할까요? (↑/↓, Enter):",
        choices=["1. No", "2. Yes"],
        use_shortcuts=True,
    ).ask()
    return choice == "2. Yes"


def _call_runner(func, input_context: dict) -> int:
    """Call runner with or without input_context based on signature."""
    try:
        import inspect
        if len(inspect.signature(func).parameters) >= 1:
            result = func(input_context)
        else:
            result = func()
    except Exception:
        raise
    return int(result) if isinstance(result, int) else 0


def main() -> None:
    cli()


if __name__ == "__main__":
    main()


# top-level 서브커맨드로 `gar init` 등록
cli.add_command(init_command, name="init")
