"""Editor CLI commands."""

import sys
import click
from typing import Optional, Any

from cli.utils.config import get_config
from cli.utils.output import format_output, print_error, print_success, print_info
from cli.utils.connection import run_command, run_list_custom_tools, handle_unity_errors, UnityConnectionError
from cli.utils.suggestions import suggest_matches, format_suggestions
from cli.utils.parsers import parse_json_dict_or_exit


@click.group()
def editor():
    """Editor operations - play mode, console, tags, layers."""
    pass


@editor.command("play")
@handle_unity_errors
def play():
    """Enter play mode."""
    config = get_config()
    result = run_command("manage_editor", {"action": "play"}, config)
    click.echo(format_output(result, config.format))
    if result.get("success"):
        print_success("Entered play mode")


@editor.command("pause")
@handle_unity_errors
def pause():
    """Pause play mode."""
    config = get_config()
    result = run_command("manage_editor", {"action": "pause"}, config)
    click.echo(format_output(result, config.format))
    if result.get("success"):
        print_success("Paused play mode")


@editor.command("stop")
@handle_unity_errors
def stop():
    """Stop play mode."""
    config = get_config()
    result = run_command("manage_editor", {"action": "stop"}, config)
    click.echo(format_output(result, config.format))
    if result.get("success"):
        print_success("Stopped play mode")


@editor.command("console")
@click.option(
    "--type", "-t",
    "log_types",
    multiple=True,
    type=click.Choice(["error", "warning", "log", "all"]),
    default=["error", "warning", "log"],
    help="Message types to retrieve."
)
@click.option(
    "--count", "-n",
    default=10,
    type=int,
    help="Number of messages to retrieve."
)
@click.option(
    "--filter", "-f",
    "filter_text",
    default=None,
    help="Filter messages containing this text."
)
@click.option(
    "--stacktrace", "-s",
    is_flag=True,
    help="Include stack traces."
)
@click.option(
    "--clear",
    is_flag=True,
    help="Clear the console instead of reading."
)
@handle_unity_errors
def console(log_types: tuple, count: int, filter_text: Optional[str], stacktrace: bool, clear: bool):
    """Read or clear the Unity console.

    \b
    Examples:
        unity-mcp editor console
        unity-mcp editor console --type error --count 20
        unity-mcp editor console --filter "NullReference" --stacktrace
        unity-mcp editor console --clear
    """
    config = get_config()

    if clear:
        result = run_command("read_console", {"action": "clear"}, config)
        click.echo(format_output(result, config.format))
        if result.get("success"):
            print_success("Console cleared")
        return

    params: dict[str, Any] = {
        "action": "get",
        "types": list(log_types),
        "count": count,
        "include_stacktrace": stacktrace,
    }

    if filter_text:
        params["filter_text"] = filter_text

    result = run_command("read_console", params, config)
    click.echo(format_output(result, config.format))


@editor.command("add-tag")
@click.argument("tag_name")
@handle_unity_errors
def add_tag(tag_name: str):
    """Add a new tag.

    \b
    Examples:
        unity-mcp editor add-tag "Enemy"
        unity-mcp editor add-tag "Collectible"
    """
    config = get_config()
    result = run_command(
        "manage_editor", {"action": "add_tag", "tagName": tag_name}, config)
    click.echo(format_output(result, config.format))
    if result.get("success"):
        print_success(f"Added tag: {tag_name}")


@editor.command("remove-tag")
@click.argument("tag_name")
@handle_unity_errors
def remove_tag(tag_name: str):
    """Remove a tag.

    \b
    Examples:
        unity-mcp editor remove-tag "OldTag"
    """
    config = get_config()
    result = run_command(
        "manage_editor", {"action": "remove_tag", "tagName": tag_name}, config)
    click.echo(format_output(result, config.format))
    if result.get("success"):
        print_success(f"Removed tag: {tag_name}")


@editor.command("add-layer")
@click.argument("layer_name")
@handle_unity_errors
def add_layer(layer_name: str):
    """Add a new layer.

    \b
    Examples:
        unity-mcp editor add-layer "Interactable"
    """
    config = get_config()
    result = run_command(
        "manage_editor", {"action": "add_layer", "layerName": layer_name}, config)
    click.echo(format_output(result, config.format))
    if result.get("success"):
        print_success(f"Added layer: {layer_name}")


@editor.command("remove-layer")
@click.argument("layer_name")
@handle_unity_errors
def remove_layer(layer_name: str):
    """Remove a layer.

    \b
    Examples:
        unity-mcp editor remove-layer "OldLayer"
    """
    config = get_config()
    result = run_command(
        "manage_editor", {"action": "remove_layer", "layerName": layer_name}, config)
    click.echo(format_output(result, config.format))
    if result.get("success"):
        print_success(f"Removed layer: {layer_name}")


@editor.command("tool")
@click.argument("tool_name")
@handle_unity_errors
def set_tool(tool_name: str):
    """Set the active editor tool.

    \b
    Examples:
        unity-mcp editor tool "Move"
        unity-mcp editor tool "Rotate"
        unity-mcp editor tool "Scale"
    """
    config = get_config()
    result = run_command(
        "manage_editor", {"action": "set_active_tool", "toolName": tool_name}, config)
    click.echo(format_output(result, config.format))
    if result.get("success"):
        print_success(f"Set active tool: {tool_name}")


@editor.command("menu")
@click.argument("menu_path")
@handle_unity_errors
def execute_menu(menu_path: str):
    """Execute a menu item.

    \b
    Examples:
        unity-mcp editor menu "File/Save"
        unity-mcp editor menu "Edit/Undo"
        unity-mcp editor menu "GameObject/Create Empty"
    """
    config = get_config()
    result = run_command("execute_menu_item", {"menu_path": menu_path}, config)
    click.echo(format_output(result, config.format))
    if result.get("success"):
        print_success(f"Executed: {menu_path}")


@editor.command("tests")
@click.option(
    "--mode", "-m",
    type=click.Choice(["EditMode", "PlayMode"]),
    default="EditMode",
    help="Test mode to run."
)
@click.option(
    "--async", "async_mode",
    is_flag=True,
    help="Run asynchronously and return job ID for polling."
)
@click.option(
    "--wait", "-w",
    type=int,
    default=None,
    help="Wait up to N seconds for completion (default: no wait)."
)
@click.option(
    "--details",
    is_flag=True,
    help="Include detailed results for all tests."
)
@click.option(
    "--failed-only",
    is_flag=True,
    help="Include details for failed/skipped tests only."
)
@handle_unity_errors
def run_tests(mode: str, async_mode: bool, wait: Optional[int], details: bool, failed_only: bool):
    """Run Unity tests.

    \b
    Examples:
        unity-mcp editor tests
        unity-mcp editor tests --mode PlayMode
        unity-mcp editor tests --async
        unity-mcp editor tests --wait 60 --failed-only
    """
    config = get_config()

    params: dict[str, Any] = {"mode": mode}
    if wait is not None:
        params["wait_timeout"] = wait
    if details:
        params["include_details"] = True
    if failed_only:
        params["include_failed_tests"] = True

    result = run_command("run_tests", params, config)

    # For async mode, just show job ID
    if async_mode and result.get("success"):
        job_id = result.get("data", {}).get("job_id")
        if job_id:
            click.echo(f"Test job started: {job_id}")
            print_info("Poll with: unity-mcp editor poll-test " + job_id)
            return

    click.echo(format_output(result, config.format))


@editor.command("poll-test")
@click.argument("job_id")
@click.option(
    "--wait", "-w",
    type=int,
    default=30,
    help="Wait up to N seconds for completion (default: 30)."
)
@click.option(
    "--details",
    is_flag=True,
    help="Include detailed results for all tests."
)
@click.option(
    "--failed-only",
    is_flag=True,
    help="Include details for failed/skipped tests only."
)
@handle_unity_errors
def poll_test(job_id: str, wait: int, details: bool, failed_only: bool):
    """Poll an async test job for status/results.

    \b
    Examples:
        unity-mcp editor poll-test abc123
        unity-mcp editor poll-test abc123 --wait 60
        unity-mcp editor poll-test abc123 --failed-only
    """
    config = get_config()

    params: dict[str, Any] = {"job_id": job_id}
    if wait:
        params["wait_timeout"] = wait
    if details:
        params["include_details"] = True
    if failed_only:
        params["include_failed_tests"] = True

    result = run_command("get_test_job", params, config)
    click.echo(format_output(result, config.format))

    if isinstance(result, dict) and result.get("success"):
        data = result.get("data", {})
        status = data.get("status", "unknown")
        if status == "succeeded":
            print_success("Tests completed successfully")
        elif status == "failed":
            summary = data.get("result", {}).get("summary", {})
            failed = summary.get("failed", 0)
            print_error(f"Tests failed: {failed} failures")
        elif status == "running":
            progress = data.get("progress", {})
            completed = progress.get("completed", 0)
            total = progress.get("total", 0)
            print_info(f"Tests running: {completed}/{total}")


@editor.command("refresh")
@click.option(
    "--mode",
    type=click.Choice(["if_dirty", "force"]),
    default="if_dirty",
    help="Refresh mode."
)
@click.option(
    "--scope",
    type=click.Choice(["assets", "scripts", "all"]),
    default="all",
    help="What to refresh."
)
@click.option(
    "--compile",
    is_flag=True,
    help="Request script compilation."
)
@click.option(
    "--no-wait",
    is_flag=True,
    help="Don't wait for refresh to complete."
)
@handle_unity_errors
def refresh(mode: str, scope: str, compile: bool, no_wait: bool):
    """Force Unity to refresh assets/scripts.

    \b
    Examples:
        unity-mcp editor refresh
        unity-mcp editor refresh --mode force
        unity-mcp editor refresh --compile
        unity-mcp editor refresh --scope scripts --compile
    """
    config = get_config()

    params: dict[str, Any] = {
        "mode": mode,
        "scope": scope,
        "wait_for_ready": not no_wait,
    }
    if compile:
        params["compile"] = "request"

    click.echo("Refreshing Unity...")
    result = run_command("refresh_unity", params, config)
    click.echo(format_output(result, config.format))
    if result.get("success"):
        print_success("Unity refreshed")


@editor.command("custom-tool")
@click.argument("tool_name")
@click.option(
    "--params", "-p",
    default="{}",
    help="Tool parameters as JSON."
)
@handle_unity_errors
def custom_tool(tool_name: str, params: str):
    """Execute a custom Unity tool.

    Custom tools are registered by Unity projects via the MCP plugin.

    \b
    Examples:
        unity-mcp editor custom-tool "MyCustomTool"
        unity-mcp editor custom-tool "BuildPipeline" --params '{"target": "Android"}'
    """
    config = get_config()

    params_dict = parse_json_dict_or_exit(params, "params")

    result = run_command("execute_custom_tool", {
        "tool_name": tool_name,
        "parameters": params_dict,
    }, config)
    click.echo(format_output(result, config.format))
    if result.get("success"):
        print_success(f"Executed custom tool: {tool_name}")
    else:
        message = (result.get("message") or result.get("error") or "").lower()
        if "not found" in message and "tool" in message:
            try:
                tools_result = run_list_custom_tools(config)
                tools = tools_result.get("tools")
                if tools is None:
                    data = tools_result.get("data", {})
                    tools = data.get("tools") if isinstance(data, dict) else None
                names = [
                    t.get("name") for t in tools if isinstance(t, dict) and t.get("name")
                ] if isinstance(tools, list) else []
                matches = suggest_matches(tool_name, names)
                suggestion = format_suggestions(matches)
                if suggestion:
                    print_info(suggestion)
                    print_info(f'Example: unity-mcp editor custom-tool "{matches[0]}"')
            except UnityConnectionError:
                pass
