#!/usr/bin/env python3
"""
Bitbucket MCP Server - Manage Pull Requests on Bitbucket Cloud

Tools:
- setup_bitbucket: Configure Bitbucket credentials
- get_config_status: Check configuration status
- list_workspace_members: List workspace members (for finding reviewers)
- get_default_reviewers: Get default reviewers for a repository
- create_pull_request: Create a new PR (with default reviewers support)
- list_pull_requests: List PRs for a repository
- get_pull_request: View details of a specific PR
- update_pull_request: Edit an existing PR (including reviewers)
- approve_pull_request: Approve a PR
- unapprove_pull_request: Remove approval from a PR
- request_changes_pull_request: Request changes on a PR
- add_pull_request_comment: Add a comment to a PR
- get_pull_request_diffstat: Get summary of files changed in a PR
- get_pull_request_diff: Get the code diff for a PR (full or per-file)
- get_file_contents: Get file contents from a branch/commit
"""

import json
import os
import stat
from pathlib import Path
from typing import Optional

import httpx
from fastmcp import FastMCP

# Initialize FastMCP server
mcp = FastMCP("bitbucket")

# Config file location
CONFIG_DIR = Path.home() / ".bitbucket-mcp"
CONFIG_FILE = CONFIG_DIR / "config.json"

# Bitbucket API base URL
BITBUCKET_API = "https://api.bitbucket.org/2.0"


def load_config() -> dict:
    """Load configuration from file."""
    if CONFIG_FILE.exists():
        with open(CONFIG_FILE, "r") as f:
            return json.load(f)
    return {}


def save_config(config: dict) -> None:
    """Save configuration to file with secure permissions."""
    CONFIG_DIR.mkdir(parents=True, exist_ok=True)

    with open(CONFIG_FILE, "w") as f:
        json.dump(config, f, indent=2)

    # Set file permissions to 600 (owner read/write only)
    os.chmod(CONFIG_FILE, stat.S_IRUSR | stat.S_IWUSR)


def get_auth(token: Optional[str] = None, username: Optional[str] = None) -> httpx.BasicAuth:
    """Get Basic Auth for Bitbucket API (Atlassian API Token)."""
    config = load_config()
    auth_token = token or config.get("token") or os.environ.get("BITBUCKET_API_TOKEN")
    auth_user = username or config.get("username") or os.environ.get("BITBUCKET_USERNAME")

    if not auth_token or not auth_user:
        raise ValueError("No Bitbucket credentials configured. Please run setup_bitbucket first.")

    return httpx.BasicAuth(auth_user, auth_token)


def get_workspace(workspace: Optional[str] = None) -> str:
    """Get workspace from parameter, config, or environment."""
    config = load_config()
    ws = workspace or config.get("workspace") or os.environ.get("BITBUCKET_WORKSPACE")

    if not ws:
        raise ValueError("No workspace specified. Please provide workspace parameter or run setup_bitbucket.")

    return ws


@mcp.tool()
def setup_bitbucket(workspace: str, username: str, api_token: str) -> str:
    """
    Configure Bitbucket credentials for the MCP server.

    Args:
        workspace: Bitbucket workspace slug (e.g., "myworkspace")
        username: Your Atlassian account email (e.g., "user@example.com")
        api_token: Atlassian API Token from https://id.atlassian.com/manage-profile/security/api-tokens

    Returns:
        Success or error message
    """
    try:
        # Validate credentials by making a test API call with Basic Auth
        auth = httpx.BasicAuth(username, api_token)

        with httpx.Client() as client:
            # First try to get user info to validate token
            user_response = client.get(
                f"{BITBUCKET_API}/user",
                auth=auth,
                timeout=30.0
            )

            if user_response.status_code == 401:
                return "Error: Invalid credentials. Please check your email and API token."

            # Then validate workspace access
            ws_response = client.get(
                f"{BITBUCKET_API}/workspaces/{workspace}",
                auth=auth,
                timeout=30.0
            )

        if ws_response.status_code == 404:
            return f"Error: Workspace '{workspace}' not found or you don't have access to it."
        elif ws_response.status_code == 403:
            return f"Error: No permission to access workspace '{workspace}'."
        elif ws_response.status_code != 200:
            return f"Error: Failed to validate workspace. Status: {ws_response.status_code} - {ws_response.text}"

        # Save configuration
        config = {
            "workspace": workspace,
            "username": username,
            "token": api_token
        }
        save_config(config)

        return f"Successfully configured Bitbucket for workspace '{workspace}'. Configuration saved to {CONFIG_FILE}"

    except httpx.RequestError as e:
        return f"Error: Failed to connect to Bitbucket API: {str(e)}"
    except Exception as e:
        return f"Error: {str(e)}"


@mcp.tool()
def get_config_status() -> dict:
    """
    Check if Bitbucket is configured and return current status.

    Returns:
        Configuration status with workspace info
    """
    config = load_config()
    env_token = os.environ.get("BITBUCKET_API_TOKEN")
    env_workspace = os.environ.get("BITBUCKET_WORKSPACE")
    env_username = os.environ.get("BITBUCKET_USERNAME")

    has_token = bool(config.get("token") or env_token)
    has_username = bool(config.get("username") or env_username)
    configured = has_token and has_username
    workspace = config.get("workspace") or env_workspace
    username = config.get("username") or env_username

    return {
        "configured": configured,
        "workspace": workspace,
        "username": username,
        "config_file": str(CONFIG_FILE),
        "source": "config_file" if config.get("token") else ("environment" if env_token else None)
    }


@mcp.tool()
def list_workspace_members(
    workspace: Optional[str] = None,
    page: int = 1,
    pagelen: int = 50
) -> dict:
    """
    List members of a Bitbucket workspace. Use this to find users who can be added as reviewers.

    Args:
        workspace: Bitbucket workspace (optional if configured)
        page: Page number for pagination (default: 1)
        pagelen: Number of results per page, max 100 (default: 50)

    Returns:
        List of workspace members with their UUIDs and display names
    """
    try:
        ws = get_workspace(workspace)
        auth = get_auth()

        params = {
            "page": page,
            "pagelen": min(pagelen, 100)
        }

        with httpx.Client() as client:
            response = client.get(
                f"{BITBUCKET_API}/workspaces/{ws}/members",
                auth=auth,
                params=params,
                timeout=30.0
            )

        if response.status_code == 200:
            data = response.json()
            members = []
            for member in data.get("values", []):
                user = member.get("user", {})
                members.append({
                    "uuid": user.get("uuid"),
                    "account_id": user.get("account_id"),
                    "display_name": user.get("display_name"),
                    "nickname": user.get("nickname")
                })
            return {
                "success": True,
                "members": members,
                "total": data.get("size", len(members)),
                "page": data.get("page", page),
                "pagelen": data.get("pagelen", pagelen)
            }
        elif response.status_code == 401:
            return {"success": False, "error": "Authentication failed. Please reconfigure with setup_bitbucket."}
        elif response.status_code == 404:
            return {"success": False, "error": f"Workspace '{ws}' not found."}
        else:
            return {"success": False, "error": f"API error: {response.status_code} - {response.text}"}

    except ValueError as e:
        return {"success": False, "error": str(e)}
    except httpx.RequestError as e:
        return {"success": False, "error": f"Connection error: {str(e)}"}
    except Exception as e:
        return {"success": False, "error": f"Unexpected error: {str(e)}"}


@mcp.tool()
def get_default_reviewers(
    repo_slug: str,
    workspace: Optional[str] = None
) -> dict:
    """
    Get the default reviewers configured for a repository.

    Args:
        repo_slug: Repository slug (name)
        workspace: Bitbucket workspace (optional if configured)

    Returns:
        List of default reviewers with their UUIDs and display names
    """
    try:
        ws = get_workspace(workspace)
        auth = get_auth()

        with httpx.Client() as client:
            response = client.get(
                f"{BITBUCKET_API}/repositories/{ws}/{repo_slug}/default-reviewers",
                auth=auth,
                timeout=30.0
            )

        if response.status_code == 200:
            data = response.json()
            reviewers = []
            for reviewer in data.get("values", []):
                reviewers.append({
                    "uuid": reviewer.get("uuid"),
                    "account_id": reviewer.get("account_id"),
                    "display_name": reviewer.get("display_name"),
                    "nickname": reviewer.get("nickname")
                })
            return {
                "success": True,
                "default_reviewers": reviewers,
                "count": len(reviewers)
            }
        elif response.status_code == 401:
            return {"success": False, "error": "Authentication failed. Please reconfigure with setup_bitbucket."}
        elif response.status_code == 404:
            return {"success": False, "error": f"Repository '{ws}/{repo_slug}' not found."}
        else:
            return {"success": False, "error": f"API error: {response.status_code} - {response.text}"}

    except ValueError as e:
        return {"success": False, "error": str(e)}
    except httpx.RequestError as e:
        return {"success": False, "error": f"Connection error: {str(e)}"}
    except Exception as e:
        return {"success": False, "error": f"Unexpected error: {str(e)}"}


@mcp.tool()
def create_pull_request(
    repo_slug: str,
    title: str,
    source_branch: str,
    destination_branch: str = "main",
    description: str = "",
    reviewers: Optional[list[str]] = None,
    use_default_reviewers: bool = True,
    workspace: Optional[str] = None
) -> dict:
    """
    Create a Pull Request on Bitbucket Cloud.

    Args:
        repo_slug: Repository slug (name)
        title: PR title
        source_branch: Branch to merge from
        destination_branch: Branch to merge into (default: main)
        description: PR description (optional)
        reviewers: List of reviewer UUIDs or account_ids (optional).
                   These are added in addition to default reviewers.
        use_default_reviewers: Whether to include default reviewers (default: True)
        workspace: Bitbucket workspace (optional if configured)

    Returns:
        PR details including URL and ID, or error message
    """
    try:
        ws = get_workspace(workspace)
        auth = get_auth()

        payload = {
            "title": title,
            "source": {
                "branch": {
                    "name": source_branch
                }
            },
            "destination": {
                "branch": {
                    "name": destination_branch
                }
            }
        }

        if description:
            payload["description"] = description

        # Collect reviewers
        reviewer_list = []

        # Add default reviewers if requested
        if use_default_reviewers:
            with httpx.Client() as client:
                default_response = client.get(
                    f"{BITBUCKET_API}/repositories/{ws}/{repo_slug}/default-reviewers",
                    auth=auth,
                    timeout=30.0
                )
            if default_response.status_code == 200:
                default_data = default_response.json()
                for reviewer in default_data.get("values", []):
                    reviewer_list.append({"uuid": reviewer.get("uuid")})

        # Add explicitly specified reviewers
        if reviewers:
            for r in reviewers:
                if r.startswith("{"):
                    reviewer_list.append({"uuid": r})
                else:
                    reviewer_list.append({"account_id": r})

        # Remove duplicates by uuid
        seen_uuids = set()
        unique_reviewers = []
        for r in reviewer_list:
            uuid = r.get("uuid") or r.get("account_id")
            if uuid and uuid not in seen_uuids:
                seen_uuids.add(uuid)
                unique_reviewers.append(r)

        if unique_reviewers:
            payload["reviewers"] = unique_reviewers

        with httpx.Client() as client:
            response = client.post(
                f"{BITBUCKET_API}/repositories/{ws}/{repo_slug}/pullrequests",
                auth=auth,
                json=payload,
                timeout=30.0
            )

        if response.status_code == 201:
            data = response.json()
            pr_reviewers = [
                {"display_name": r.get("display_name"), "uuid": r.get("uuid")}
                for r in data.get("reviewers", [])
            ]
            return {
                "success": True,
                "id": data.get("id"),
                "title": data.get("title"),
                "url": data.get("links", {}).get("html", {}).get("href"),
                "state": data.get("state"),
                "source_branch": source_branch,
                "destination_branch": destination_branch,
                "reviewers": pr_reviewers
            }
        elif response.status_code == 400:
            error_data = response.json()
            error_msg = error_data.get("error", {}).get("message", "Bad request")
            return {"success": False, "error": error_msg}
        elif response.status_code == 401:
            return {"success": False, "error": "Authentication failed. Please reconfigure with setup_bitbucket."}
        elif response.status_code == 404:
            return {"success": False, "error": f"Repository '{ws}/{repo_slug}' not found."}
        else:
            return {"success": False, "error": f"API error: {response.status_code} - {response.text}"}

    except ValueError as e:
        return {"success": False, "error": str(e)}
    except httpx.RequestError as e:
        return {"success": False, "error": f"Connection error: {str(e)}"}
    except Exception as e:
        return {"success": False, "error": f"Unexpected error: {str(e)}"}


@mcp.tool()
def list_pull_requests(
    repo_slug: str,
    state: str = "OPEN",
    workspace: Optional[str] = None,
    page: int = 1,
    pagelen: int = 25
) -> dict:
    """
    List Pull Requests for a repository on Bitbucket Cloud.

    Args:
        repo_slug: Repository slug (name)
        state: PR state filter - OPEN, MERGED, DECLINED, SUPERSEDED, or ALL (default: OPEN)
        workspace: Bitbucket workspace (optional if configured)
        page: Page number for pagination (default: 1)
        pagelen: Number of results per page, max 50 (default: 25)

    Returns:
        List of PRs with their details
    """
    try:
        ws = get_workspace(workspace)
        auth = get_auth()

        params = {
            "page": page,
            "pagelen": min(pagelen, 50)
        }

        if state.upper() != "ALL":
            params["state"] = state.upper()

        with httpx.Client() as client:
            response = client.get(
                f"{BITBUCKET_API}/repositories/{ws}/{repo_slug}/pullrequests",
                auth=auth,
                params=params,
                timeout=30.0
            )

        if response.status_code == 200:
            data = response.json()
            prs = []
            for pr in data.get("values", []):
                prs.append({
                    "id": pr.get("id"),
                    "title": pr.get("title"),
                    "state": pr.get("state"),
                    "author": pr.get("author", {}).get("display_name"),
                    "source_branch": pr.get("source", {}).get("branch", {}).get("name"),
                    "destination_branch": pr.get("destination", {}).get("branch", {}).get("name"),
                    "url": pr.get("links", {}).get("html", {}).get("href"),
                    "created_on": pr.get("created_on"),
                    "updated_on": pr.get("updated_on")
                })
            return {
                "success": True,
                "pull_requests": prs,
                "total": data.get("size", len(prs)),
                "page": data.get("page", page),
                "pagelen": data.get("pagelen", pagelen)
            }
        elif response.status_code == 401:
            return {"success": False, "error": "Authentication failed. Please reconfigure with setup_bitbucket."}
        elif response.status_code == 404:
            return {"success": False, "error": f"Repository '{ws}/{repo_slug}' not found."}
        else:
            return {"success": False, "error": f"API error: {response.status_code} - {response.text}"}

    except ValueError as e:
        return {"success": False, "error": str(e)}
    except httpx.RequestError as e:
        return {"success": False, "error": f"Connection error: {str(e)}"}
    except Exception as e:
        return {"success": False, "error": f"Unexpected error: {str(e)}"}


@mcp.tool()
def get_pull_request(
    repo_slug: str,
    pr_id: int,
    workspace: Optional[str] = None
) -> dict:
    """
    Get details of a specific Pull Request on Bitbucket Cloud.

    Args:
        repo_slug: Repository slug (name)
        pr_id: Pull Request ID
        workspace: Bitbucket workspace (optional if configured)

    Returns:
        Detailed PR information including description, reviewers, and comments count
    """
    try:
        ws = get_workspace(workspace)
        auth = get_auth()

        with httpx.Client() as client:
            response = client.get(
                f"{BITBUCKET_API}/repositories/{ws}/{repo_slug}/pullrequests/{pr_id}",
                auth=auth,
                timeout=30.0
            )

        if response.status_code == 200:
            pr = response.json()
            reviewers = []
            for reviewer in pr.get("reviewers", []):
                reviewers.append({
                    "display_name": reviewer.get("display_name"),
                    "uuid": reviewer.get("uuid"),
                    "approved": reviewer.get("approved", False)
                })

            participants = []
            for participant in pr.get("participants", []):
                participants.append({
                    "display_name": participant.get("user", {}).get("display_name"),
                    "role": participant.get("role"),
                    "approved": participant.get("approved", False),
                    "state": participant.get("state")
                })

            return {
                "success": True,
                "id": pr.get("id"),
                "title": pr.get("title"),
                "description": pr.get("description", ""),
                "state": pr.get("state"),
                "author": pr.get("author", {}).get("display_name"),
                "source_branch": pr.get("source", {}).get("branch", {}).get("name"),
                "destination_branch": pr.get("destination", {}).get("branch", {}).get("name"),
                "url": pr.get("links", {}).get("html", {}).get("href"),
                "created_on": pr.get("created_on"),
                "updated_on": pr.get("updated_on"),
                "close_source_branch": pr.get("close_source_branch", False),
                "comment_count": pr.get("comment_count", 0),
                "task_count": pr.get("task_count", 0),
                "reviewers": reviewers,
                "participants": participants,
                "merge_commit": pr.get("merge_commit", {}).get("hash") if pr.get("merge_commit") else None
            }
        elif response.status_code == 401:
            return {"success": False, "error": "Authentication failed. Please reconfigure with setup_bitbucket."}
        elif response.status_code == 404:
            return {"success": False, "error": f"Pull request #{pr_id} not found in '{ws}/{repo_slug}'."}
        else:
            return {"success": False, "error": f"API error: {response.status_code} - {response.text}"}

    except ValueError as e:
        return {"success": False, "error": str(e)}
    except httpx.RequestError as e:
        return {"success": False, "error": f"Connection error: {str(e)}"}
    except Exception as e:
        return {"success": False, "error": f"Unexpected error: {str(e)}"}


@mcp.tool()
def update_pull_request(
    repo_slug: str,
    pr_id: int,
    title: Optional[str] = None,
    description: Optional[str] = None,
    destination_branch: Optional[str] = None,
    reviewers: Optional[list[str]] = None,
    close_source_branch: Optional[bool] = None,
    workspace: Optional[str] = None
) -> dict:
    """
    Update/edit an existing Pull Request on Bitbucket Cloud.

    Args:
        repo_slug: Repository slug (name)
        pr_id: Pull Request ID to update
        title: New PR title (optional)
        description: New PR description (optional)
        destination_branch: New destination branch (optional)
        reviewers: List of reviewer UUIDs or account_ids to set as reviewers (optional).
                   Pass empty list [] to remove all reviewers.
        close_source_branch: Whether to close source branch on merge (optional)
        workspace: Bitbucket workspace (optional if configured)

    Returns:
        Updated PR details or error message
    """
    try:
        ws = get_workspace(workspace)
        auth = get_auth()

        # Build payload with only provided fields
        payload = {}
        if title is not None:
            payload["title"] = title
        if description is not None:
            payload["description"] = description
        if destination_branch is not None:
            payload["destination"] = {"branch": {"name": destination_branch}}
        if reviewers is not None:
            payload["reviewers"] = [{"uuid": r} if r.startswith("{") else {"account_id": r} for r in reviewers]
        if close_source_branch is not None:
            payload["close_source_branch"] = close_source_branch

        if not payload:
            return {"success": False, "error": "No update fields provided. Specify at least one of: title, description, destination_branch, reviewers, close_source_branch"}

        with httpx.Client() as client:
            response = client.put(
                f"{BITBUCKET_API}/repositories/{ws}/{repo_slug}/pullrequests/{pr_id}",
                auth=auth,
                json=payload,
                timeout=30.0
            )

        if response.status_code == 200:
            pr = response.json()
            reviewer_list = [
                {"display_name": r.get("display_name"), "uuid": r.get("uuid")}
                for r in pr.get("reviewers", [])
            ]
            return {
                "success": True,
                "id": pr.get("id"),
                "title": pr.get("title"),
                "description": pr.get("description", ""),
                "state": pr.get("state"),
                "source_branch": pr.get("source", {}).get("branch", {}).get("name"),
                "destination_branch": pr.get("destination", {}).get("branch", {}).get("name"),
                "url": pr.get("links", {}).get("html", {}).get("href"),
                "close_source_branch": pr.get("close_source_branch", False),
                "reviewers": reviewer_list,
                "updated_on": pr.get("updated_on")
            }
        elif response.status_code == 400:
            error_data = response.json()
            error_msg = error_data.get("error", {}).get("message", "Bad request")
            return {"success": False, "error": error_msg}
        elif response.status_code == 401:
            return {"success": False, "error": "Authentication failed. Please reconfigure with setup_bitbucket."}
        elif response.status_code == 404:
            return {"success": False, "error": f"Pull request #{pr_id} not found in '{ws}/{repo_slug}'."}
        elif response.status_code == 403:
            return {"success": False, "error": "Permission denied. You may not have write access to this PR."}
        else:
            return {"success": False, "error": f"API error: {response.status_code} - {response.text}"}

    except ValueError as e:
        return {"success": False, "error": str(e)}
    except httpx.RequestError as e:
        return {"success": False, "error": f"Connection error: {str(e)}"}
    except Exception as e:
        return {"success": False, "error": f"Unexpected error: {str(e)}"}


@mcp.tool()
def approve_pull_request(
    repo_slug: str,
    pr_id: int,
    workspace: Optional[str] = None
) -> dict:
    """
    Approve a Pull Request on Bitbucket Cloud.

    Args:
        repo_slug: Repository slug (name)
        pr_id: Pull Request ID to approve
        workspace: Bitbucket workspace (optional if configured)

    Returns:
        Approval confirmation or error message
    """
    try:
        ws = get_workspace(workspace)
        auth = get_auth()

        with httpx.Client() as client:
            response = client.post(
                f"{BITBUCKET_API}/repositories/{ws}/{repo_slug}/pullrequests/{pr_id}/approve",
                auth=auth,
                timeout=30.0
            )

        if response.status_code in (200, 201):
            data = response.json()
            return {
                "success": True,
                "message": f"Pull request #{pr_id} approved successfully",
                "approved": data.get("approved", True),
                "user": data.get("user", {}).get("display_name")
            }
        elif response.status_code == 401:
            return {"success": False, "error": "Authentication failed. Please reconfigure with setup_bitbucket."}
        elif response.status_code == 404:
            return {"success": False, "error": f"Pull request #{pr_id} not found in '{ws}/{repo_slug}'."}
        elif response.status_code == 409:
            return {"success": False, "error": "You have already approved this pull request."}
        else:
            return {"success": False, "error": f"API error: {response.status_code} - {response.text}"}

    except ValueError as e:
        return {"success": False, "error": str(e)}
    except httpx.RequestError as e:
        return {"success": False, "error": f"Connection error: {str(e)}"}
    except Exception as e:
        return {"success": False, "error": f"Unexpected error: {str(e)}"}


@mcp.tool()
def unapprove_pull_request(
    repo_slug: str,
    pr_id: int,
    workspace: Optional[str] = None
) -> dict:
    """
    Remove approval from a Pull Request on Bitbucket Cloud.

    Args:
        repo_slug: Repository slug (name)
        pr_id: Pull Request ID to unapprove
        workspace: Bitbucket workspace (optional if configured)

    Returns:
        Confirmation or error message
    """
    try:
        ws = get_workspace(workspace)
        auth = get_auth()

        with httpx.Client() as client:
            response = client.delete(
                f"{BITBUCKET_API}/repositories/{ws}/{repo_slug}/pullrequests/{pr_id}/approve",
                auth=auth,
                timeout=30.0
            )

        if response.status_code in (200, 204):
            return {
                "success": True,
                "message": f"Approval removed from pull request #{pr_id}"
            }
        elif response.status_code == 401:
            return {"success": False, "error": "Authentication failed. Please reconfigure with setup_bitbucket."}
        elif response.status_code == 404:
            return {"success": False, "error": f"Pull request #{pr_id} not found or not previously approved."}
        else:
            return {"success": False, "error": f"API error: {response.status_code} - {response.text}"}

    except ValueError as e:
        return {"success": False, "error": str(e)}
    except httpx.RequestError as e:
        return {"success": False, "error": f"Connection error: {str(e)}"}
    except Exception as e:
        return {"success": False, "error": f"Unexpected error: {str(e)}"}


@mcp.tool()
def request_changes_pull_request(
    repo_slug: str,
    pr_id: int,
    workspace: Optional[str] = None
) -> dict:
    """
    Request changes on a Pull Request on Bitbucket Cloud.

    Args:
        repo_slug: Repository slug (name)
        pr_id: Pull Request ID
        workspace: Bitbucket workspace (optional if configured)

    Returns:
        Confirmation or error message
    """
    try:
        ws = get_workspace(workspace)
        auth = get_auth()

        with httpx.Client() as client:
            response = client.post(
                f"{BITBUCKET_API}/repositories/{ws}/{repo_slug}/pullrequests/{pr_id}/request-changes",
                auth=auth,
                timeout=30.0
            )

        if response.status_code in (200, 201):
            data = response.json()
            return {
                "success": True,
                "message": f"Changes requested on pull request #{pr_id}",
                "user": data.get("user", {}).get("display_name")
            }
        elif response.status_code == 401:
            return {"success": False, "error": "Authentication failed. Please reconfigure with setup_bitbucket."}
        elif response.status_code == 404:
            return {"success": False, "error": f"Pull request #{pr_id} not found in '{ws}/{repo_slug}'."}
        elif response.status_code == 409:
            return {"success": False, "error": "You have already requested changes on this pull request."}
        else:
            return {"success": False, "error": f"API error: {response.status_code} - {response.text}"}

    except ValueError as e:
        return {"success": False, "error": str(e)}
    except httpx.RequestError as e:
        return {"success": False, "error": f"Connection error: {str(e)}"}
    except Exception as e:
        return {"success": False, "error": f"Unexpected error: {str(e)}"}


@mcp.tool()
def add_pull_request_comment(
    repo_slug: str,
    pr_id: int,
    comment: str,
    workspace: Optional[str] = None
) -> dict:
    """
    Add a comment to a Pull Request on Bitbucket Cloud.

    Args:
        repo_slug: Repository slug (name)
        pr_id: Pull Request ID
        comment: Comment text (supports markdown)
        workspace: Bitbucket workspace (optional if configured)

    Returns:
        Comment details or error message
    """
    try:
        ws = get_workspace(workspace)
        auth = get_auth()

        payload = {
            "content": {
                "raw": comment
            }
        }

        with httpx.Client() as client:
            response = client.post(
                f"{BITBUCKET_API}/repositories/{ws}/{repo_slug}/pullrequests/{pr_id}/comments",
                auth=auth,
                json=payload,
                timeout=30.0
            )

        if response.status_code in (200, 201):
            data = response.json()
            return {
                "success": True,
                "id": data.get("id"),
                "message": f"Comment added to pull request #{pr_id}",
                "content": data.get("content", {}).get("raw"),
                "user": data.get("user", {}).get("display_name"),
                "created_on": data.get("created_on")
            }
        elif response.status_code == 401:
            return {"success": False, "error": "Authentication failed. Please reconfigure with setup_bitbucket."}
        elif response.status_code == 404:
            return {"success": False, "error": f"Pull request #{pr_id} not found in '{ws}/{repo_slug}'."}
        else:
            return {"success": False, "error": f"API error: {response.status_code} - {response.text}"}

    except ValueError as e:
        return {"success": False, "error": str(e)}
    except httpx.RequestError as e:
        return {"success": False, "error": f"Connection error: {str(e)}"}
    except Exception as e:
        return {"success": False, "error": f"Unexpected error: {str(e)}"}


@mcp.tool()
def get_pull_request_diffstat(
    repo_slug: str,
    pr_id: int,
    workspace: Optional[str] = None
) -> dict:
    """
    Get a summary of files changed in a Pull Request with line counts.

    Args:
        repo_slug: Repository slug (name)
        pr_id: Pull Request ID
        workspace: Bitbucket workspace (optional if configured)

    Returns:
        List of files with lines added/removed counts
    """
    try:
        ws = get_workspace(workspace)
        auth = get_auth()

        files = []
        next_url = f"{BITBUCKET_API}/repositories/{ws}/{repo_slug}/pullrequests/{pr_id}/diffstat"

        with httpx.Client() as client:
            while next_url:
                response = client.get(
                    next_url,
                    auth=auth,
                    timeout=30.0
                )

                if response.status_code == 200:
                    data = response.json()
                    for file_stat in data.get("values", []):
                        old_path = file_stat.get("old", {}).get("path") if file_stat.get("old") else None
                        new_path = file_stat.get("new", {}).get("path") if file_stat.get("new") else None

                        files.append({
                            "path": new_path or old_path,
                            "old_path": old_path if old_path != new_path else None,
                            "status": file_stat.get("status"),
                            "lines_added": file_stat.get("lines_added", 0),
                            "lines_removed": file_stat.get("lines_removed", 0)
                        })

                    # Handle pagination
                    next_url = data.get("next")
                elif response.status_code == 401:
                    return {"success": False, "error": "Authentication failed. Please reconfigure with setup_bitbucket."}
                elif response.status_code == 404:
                    return {"success": False, "error": f"Pull request #{pr_id} not found in '{ws}/{repo_slug}'."}
                else:
                    return {"success": False, "error": f"API error: {response.status_code} - {response.text}"}

        total_added = sum(f["lines_added"] for f in files)
        total_removed = sum(f["lines_removed"] for f in files)

        return {
            "success": True,
            "files": files,
            "total_files": len(files),
            "total_lines_added": total_added,
            "total_lines_removed": total_removed
        }

    except ValueError as e:
        return {"success": False, "error": str(e)}
    except httpx.RequestError as e:
        return {"success": False, "error": f"Connection error: {str(e)}"}
    except Exception as e:
        return {"success": False, "error": f"Unexpected error: {str(e)}"}


@mcp.tool()
def get_pull_request_diff(
    repo_slug: str,
    pr_id: int,
    file_path: Optional[str] = None,
    context_lines: int = 3,
    workspace: Optional[str] = None
) -> dict:
    """
    Get the diff (code changes) for a Pull Request.

    Args:
        repo_slug: Repository slug (name)
        pr_id: Pull Request ID
        file_path: Optional specific file path to get diff for (recommended for large PRs)
        context_lines: Number of context lines around changes (default: 3)
        workspace: Bitbucket workspace (optional if configured)

    Returns:
        The diff content as text
    """
    try:
        ws = get_workspace(workspace)
        auth = get_auth()

        # Build URL with optional path filter
        url = f"{BITBUCKET_API}/repositories/{ws}/{repo_slug}/pullrequests/{pr_id}/diff"
        if file_path:
            url = f"{url}/{file_path}"

        with httpx.Client() as client:
            response = client.get(
                url,
                auth=auth,
                headers={"Accept": "text/plain"},
                timeout=60.0  # Longer timeout for large diffs
            )

        if response.status_code == 200:
            diff_content = response.text

            # Truncate if too large (> 100KB)
            max_size = 100 * 1024
            truncated = False
            if len(diff_content) > max_size:
                diff_content = diff_content[:max_size]
                truncated = True

            return {
                "success": True,
                "diff": diff_content,
                "file_path": file_path,
                "truncated": truncated,
                "size_bytes": len(response.text)
            }
        elif response.status_code == 401:
            return {"success": False, "error": "Authentication failed. Please reconfigure with setup_bitbucket."}
        elif response.status_code == 404:
            if file_path:
                return {"success": False, "error": f"File '{file_path}' not found in PR #{pr_id}."}
            return {"success": False, "error": f"Pull request #{pr_id} not found in '{ws}/{repo_slug}'."}
        else:
            return {"success": False, "error": f"API error: {response.status_code} - {response.text}"}

    except ValueError as e:
        return {"success": False, "error": str(e)}
    except httpx.RequestError as e:
        return {"success": False, "error": f"Connection error: {str(e)}"}
    except Exception as e:
        return {"success": False, "error": f"Unexpected error: {str(e)}"}


@mcp.tool()
def get_file_contents(
    repo_slug: str,
    file_path: str,
    ref: str = "main",
    workspace: Optional[str] = None
) -> dict:
    """
    Get the contents of a file from a repository at a specific branch/commit.

    Args:
        repo_slug: Repository slug (name)
        file_path: Path to the file in the repository
        ref: Branch name, tag, or commit hash (default: main)
        workspace: Bitbucket workspace (optional if configured)

    Returns:
        File contents as text
    """
    try:
        ws = get_workspace(workspace)
        auth = get_auth()

        with httpx.Client() as client:
            response = client.get(
                f"{BITBUCKET_API}/repositories/{ws}/{repo_slug}/src/{ref}/{file_path}",
                auth=auth,
                timeout=30.0
            )

        if response.status_code == 200:
            content = response.text

            # Truncate if too large (> 100KB)
            max_size = 100 * 1024
            truncated = False
            if len(content) > max_size:
                content = content[:max_size]
                truncated = True

            return {
                "success": True,
                "content": content,
                "file_path": file_path,
                "ref": ref,
                "truncated": truncated,
                "size_bytes": len(response.text)
            }
        elif response.status_code == 401:
            return {"success": False, "error": "Authentication failed. Please reconfigure with setup_bitbucket."}
        elif response.status_code == 404:
            return {"success": False, "error": f"File '{file_path}' not found at ref '{ref}' in '{ws}/{repo_slug}'."}
        else:
            return {"success": False, "error": f"API error: {response.status_code} - {response.text}"}

    except ValueError as e:
        return {"success": False, "error": str(e)}
    except httpx.RequestError as e:
        return {"success": False, "error": f"Connection error: {str(e)}"}
    except Exception as e:
        return {"success": False, "error": f"Unexpected error: {str(e)}"}


def main():
    """Entry point for the MCP server."""
    mcp.run()


if __name__ == "__main__":
    main()
