import logging
import os
from typing import Literal, cast

from mcp.server.fastmcp import FastMCP

from agent_tools.context import REPO_CWD
from agent_tools.orchestrators import gh_pr_create, gh_pr_merge, git_commit, git_release
from agent_tools.orchestrators import git_sync as git_sync_orch

# Set up logging to stderr for MCP compatibility
logging.basicConfig(level=logging.INFO)
logger = logging.getLogger("agent-tools-mcp")

mcp = FastMCP("agent-tools")


def _with_cwd(func, repo_path: str, *args, **kwargs):
    """Context-aware wrapper to setup working directory safely using ContextVars."""
    # Priority 1: Use the explicit repo_path if it's already an absolute directory
    # Priority 2: Use AGENT_TOOLS_REPO_PATH environment variable if set
    # Priority 3: Fallback to searching upwards for .git from CWD (best effort)

    env_path = os.environ.get("AGENT_TOOLS_REPO_PATH")

    # If a path was provided and it exists, use it
    if repo_path and repo_path != "." and os.path.isdir(repo_path):
        final_path = os.path.abspath(repo_path)
    # Otherwise, if the environment variable is set, prioritize it
    elif env_path and os.path.isdir(env_path):
        final_path = os.path.abspath(env_path)
    else:
        # Fallback recursive search logic
        final_path = os.path.abspath(os.getcwd())
        current = final_path
        found = False
        while current != os.path.dirname(current):
            if os.path.isdir(os.path.join(current, ".git")):
                final_path = current
                found = True
                break
            current = os.path.dirname(current)
        if not found:
            logger.warning(f"Project root with .git not found using CWD: {os.getcwd()}")

    token = REPO_CWD.set(final_path)
    try:
        return func(*args, **kwargs)
    finally:
        REPO_CWD.reset(token)


# --- Git Commit Tools ---


@mcp.tool()
def git_commit_flow(
    point: Literal["sense", "commit"] = "sense", plan_json_str: str = ""
) -> str:
    """
    Scans the repository for changes and returns project-specific commit rules.
    Use this as the first step of any commit workflow.
    """
    res = _with_cwd(
        git_commit.git_commit_flow,
        repo_path=".",
        point=point,
        plan_json_str=plan_json_str,
    )
    return cast(str, res.to_json())


# --- Git Sync Tools ---


@mcp.tool()
def git_sync_flow(
    point: Literal["init", "current_rebase", "rebase_main", "push", "abort"] = "init",
) -> str:
    """
    Orchestrates a smart git sync (pull, rebase main, push).
    Points: 'init', 'current_rebase', 'rebase_main', 'push'
    """
    res = _with_cwd(git_sync_orch.git_sync_flow, repo_path=".", point=point)
    return cast(str, res.to_json())


# --- GitHub PR Tools ---


@mcp.tool()
def gh_pr_create_flow(
    point: Literal["init", "sense", "create"] = "init", draft_json_str: str = ""
) -> str:
    """Analyzes context for PR creation (branch, commits, diffs)."""
    res = _with_cwd(
        gh_pr_create.gh_pr_create_flow,
        repo_path=".",
        point=point,
        draft_json_str=draft_json_str,
    )
    return cast(str, res.to_json())


@mcp.tool()
def gh_pr_merge_flow(
    point: Literal["init", "sense", "merge"] = "init", override_json_str: str = ""
) -> str:
    """
    Stage 1: Analyzes PR, CI status, and reviews.
    Returns PR metadata and commit rules for synthesis.
    """
    res = _with_cwd(
        gh_pr_merge.gh_pr_merge_flow,
        repo_path=".",
        point=point,
        override_json_str=override_json_str,
    )
    return cast(str, res.to_json())


# --- Git Release Tools ---


@mcp.tool()
def git_release_flow(
    point: Literal["init", "sense", "release"] = "init", tag_json_str: str = ""
) -> str:
    """Stage 1: Analyzes commits/tags to determine next version. Use point='init' to start."""
    res = _with_cwd(
        git_release.git_release_flow,
        repo_path=".",
        point=point,
        tag_json_str=tag_json_str,
    )
    return cast(str, res.to_json())


# --- Prompts (Workflows) ---


@mcp.prompt()
def smart_commit_flow() -> str:
    return """
Follow this industrial-grade commit protocol:
1. Call `git_commit_flow(point="sense")`.
2. Use `details.commit_rules` to ensure compliance with project-specific commit rules.
"""


@mcp.prompt()
def smart_sync_flow() -> str:
    return """
Follow this linear rebase sync protocol:
1. Call `git_sync_flow(point="init")` tool.
2. If the status is 'handoff', execute the specified recovery actions before resuming.
"""


@mcp.prompt()
def smart_pr_create_flow() -> str:
    return """
Follow this standard PR creation protocol:
1. Call `gh_pr_create_flow(point="init")`.
2. Follow: ALWAYS strictly follow the dynamic `instruction` field returned by the tool.
"""


@mcp.prompt()
def smart_pr_merge_flow() -> str:
    return """
Follow this professional PR merge protocol:
1. Call `gh_pr_merge_flow(point="init")`.
2. Follow: ALWAYS strictly follow the dynamic `instruction` field returned by the tool.
"""


@mcp.prompt()
def smart_release_flow() -> str:
    return """
Follow this industrial-grade automated release protocol:
1. Call `git_release_flow(point="init")`.
2. Follow: ALWAYS strictly follow the dynamic `instruction` field returned by the tool.
"""


def main():
    # Ensure dependencies from src are discoverable if this script is run directly
    import sys

    sys.path.append(os.path.dirname(os.path.abspath(__file__)))
    mcp.run(transport="stdio")


if __name__ == "__main__":
    main()
