import os
from pathlib import Path
import urllib.parse
import httpx
import logging
from maur.proc import await_command

logger = logging.getLogger(__name__)


def _authenticated_url(repo_url: str, token: str) -> str:
    """Embed an auth token into an HTTPS git URL. Uses x-access-token for GitHub, oauth2 for others."""
    parsed = urllib.parse.urlparse(repo_url)
    if parsed.hostname and "github.com" in parsed.hostname:
        netloc = f"x-access-token:{token}@{parsed.hostname}"
    else:
        netloc = f"oauth2:{token}@{parsed.hostname}"
    return urllib.parse.urlunparse(parsed._replace(netloc=netloc))


async def setup_workspace(repo_url: str, branch: str, task_id: str, workspace_dir: Path, git_token: str | None = None):
    """Clone repo and create a new branch. Returns the repo path."""

    workspace_dir.mkdir(exist_ok=True, parents=True)

    _ = await await_command(["git", "config", "--global", "user.email", "\"maur@takk.dev\""], cwd=workspace_dir)
    _ = await await_command(["git", "config", "--global", "user.name", "\"Maur Agent\""], cwd=workspace_dir)

    clone_url = _authenticated_url(repo_url, git_token) if git_token else repo_url
    logger.debug("Cloning repo")
    _, stderr, rc = await await_command(["git", "clone", clone_url, "."], cwd=workspace_dir)
    logger.debug("Cloned repo")
    if rc != 0:
        raise RuntimeError(f"git clone failed: {stderr}")

    if git_token:
        authenticated_remote = _authenticated_url(repo_url, git_token)
        logger.debug("Authenticating")
        await await_command(["git", "remote", "set-url", "origin", authenticated_remote], cwd=workspace_dir)

    logger.debug("Creating branch")
    _, stderr, rc = await await_command(["git", "checkout", branch], cwd=workspace_dir)
    if rc != 0:
        raise RuntimeError(f"git checkout {branch} failed: {stderr}")

    result_branch = f"claude/{task_id}"
    _, stderr, rc = await await_command(["git", "checkout", "-b", result_branch], cwd=workspace_dir)
    if rc != 0:
        raise RuntimeError(f"git checkout -b {result_branch} failed: {stderr}")


async def has_changes(repo_path: Path) -> bool:
    """Return True if there are staged or unstaged changes in the repo."""
    stdout, _, _ = await await_command(["git", "status", "--porcelain"], cwd=repo_path)
    return bool(stdout.strip())


async def commit_and_push(repo_path: Path, branch: str, message: str) -> None:
    await await_command(["git", "add", "-A"], cwd=repo_path)

    _, stderr, rc = await await_command(["git", "commit", "-m", f"\"{message}\""], cwd=repo_path)
    if rc != 0:
        raise RuntimeError(f"git commit failed: {stderr}")

    _, stderr, rc = await await_command(["git", "push", "origin", branch], cwd=repo_path)
    if rc != 0:
        raise RuntimeError(f"git push failed: {stderr}")


def _project_path_from_url(repo_url: str) -> str:
    """Extract GitLab project path (namespace/repo) from a clone URL."""
    # Handle SSH: git@gitlab.com:namespace/repo.git
    if repo_url.startswith("git@"):
        path = repo_url.split(":", 1)[1]
    else:
        parsed = urllib.parse.urlparse(repo_url)
        path = parsed.path.lstrip("/")
    return path.removesuffix(".git")


async def create_gitlab_mr(
    gitlab_url: str,
    gitlab_token: str,
    repo_url: str,
    title: str,
    target_branch: str,
    description: str,
    source_branch: str
) -> str:
    """Create a GitLab MR and return its web URL."""
    project_path = _project_path_from_url(repo_url)
    encoded_path = urllib.parse.quote(project_path, safe="")

    url = f"{gitlab_url.rstrip('/')}/api/v4/projects/{encoded_path}/merge_requests"
    payload = {
        "source_branch": source_branch,
        "target_branch": target_branch,
        "title": f"fix: {title} [automated]",
        "description": description,
        "remove_source_branch": True,
    }

    async with httpx.AsyncClient() as client:
        resp = await client.post(
            url,
            json=payload,
            headers={"Authorization": f"Bearer {gitlab_token}", "Content-Type": "application/json"},
        )
        resp.raise_for_status()

    return resp.json().get("web_url", "")


async def create_github_pr(
    github_api_url: str,
    github_token: str,
    repo_url: str,
    title: str,
    target_branch: str,
    description: str,
    source_branch: str,
) -> str:
    """Create a GitHub PR and return its web URL."""
    project_path = _project_path_from_url(repo_url)
    url = f"{github_api_url.rstrip('/')}/repos/{project_path}/pulls"
    payload = {
        "head": source_branch,
        "base": target_branch,
        "title": f"fix: {title} [automated]",
        "body": description,
    }

    async with httpx.AsyncClient() as client:
        resp = await client.post(
            url,
            json=payload,
            headers={
                "Authorization": f"Bearer {github_token}",
                "Accept": "application/vnd.github+json",
                "X-GitHub-Api-Version": "2022-11-28",
            },
        )
        resp.raise_for_status()

    return resp.json().get("html_url", "")
