# #region GitServiceRemoteMixin [C:4] [TYPE Module] [SEMANTICS git, provider, github, remote, url, http_pool]
# @LAYER Infrastructure
# @BRIEF GitHub and GitLab provider operations for GitService — repository creation and PR/MR creation. Uses shared self._http_client for connection pooling.
# @RELATION CALLED_BY -> [GitService]

from typing import Any
from urllib.parse import quote

from fastapi import HTTPException

from src.core.logger import logger


# #region GitServiceGithubMixin [C:3] [TYPE Class]
# @BRIEF Mixin providing GitHub API operations for GitService.
class GitServiceGithubMixin:
    # region create_github_repository [TYPE Function]
    # @PURPOSE: Create repository in GitHub or GitHub Enterprise.
    # @PRE  PAT has repository create permission.
    # @POST  Returns created repository payload.
    # @RETURN  dict
    async def create_github_repository(
        self, server_url: str, pat: str, name: str, private: bool = True,
        description: str | None = None, auto_init: bool = True, default_branch: str | None = "main",
    ) -> dict[str, Any]:
        base_url = self._normalize_git_server_url(server_url)
        if "github.com" in base_url:
            api_url = "https://api.github.com/user/repos"
        else:
            api_url = f"{base_url}/api/v3/user/repos"
        headers = {
            "Authorization": f"token {pat.strip()}",
            "Content-Type": "application/json",
            "Accept": "application/vnd.github+json",
        }
        payload: dict[str, Any] = {"name": name, "private": bool(private), "auto_init": bool(auto_init)}
        if description:
            payload["description"] = description
        if default_branch:
            payload["default_branch"] = default_branch
        try:
            response = await self._http_client.post(api_url, headers=headers, json=payload)
        except Exception as e:
            raise HTTPException(status_code=503, detail=f"GitHub API is unavailable: {e!s}")
        if response.status_code >= 400:
            detail = response.text
            try:
                parsed = response.json()
                detail = parsed.get("message") or detail
            except Exception:
                logger.debug("Could not parse error response JSON from GitHub API")
            raise HTTPException(status_code=response.status_code, detail=f"GitHub API error: {detail}")
        return response.json()
    # endregion create_github_pull_request
# #endregion GitServiceGithubMixin

# #region GitServiceGitlabMixin [C:3] [TYPE Class]
# @BRIEF Mixin providing GitLab API operations for GitService.
class GitServiceGitlabMixin:
    # region create_gitlab_repository [TYPE Function]
    # @PURPOSE: Create repository(project) in GitLab.
    # @PRE  PAT has api scope.
    # @POST  Returns created repository payload.
    # @RETURN  dict
    async def create_gitlab_repository(
        self, server_url: str, pat: str, name: str, private: bool = True,
        description: str | None = None, auto_init: bool = True, default_branch: str | None = "main",
    ) -> dict[str, Any]:
        base_url = self._normalize_git_server_url(server_url)
        api_url = f"{base_url}/api/v4/projects"
        headers = {"PRIVATE-TOKEN": pat.strip(), "Content-Type": "application/json", "Accept": "application/json"}
        payload: dict[str, Any] = {
            "name": name, "visibility": "private" if private else "public",
            "initialize_with_readme": bool(auto_init),
        }
        if description:
            payload["description"] = description
        if default_branch:
            payload["default_branch"] = default_branch
        try:
            response = await self._http_client.post(api_url, headers=headers, json=payload)
        except Exception as e:
            raise HTTPException(status_code=503, detail=f"GitLab API is unavailable: {e!s}")
        if response.status_code >= 400:
            detail = response.text
            try:
                parsed = response.json()
                if isinstance(parsed, dict):
                    detail = parsed.get("message") or detail
            except Exception:
                pass
            raise HTTPException(status_code=response.status_code, detail=f"GitLab API error: {detail}")
        data = response.json()
        if "clone_url" not in data:
            data["clone_url"] = data.get("http_url_to_repo")
        if "html_url" not in data:
            data["html_url"] = data.get("web_url")
        if "ssh_url" not in data:
            data["ssh_url"] = data.get("ssh_url_to_repo")
        if "full_name" not in data:
            data["full_name"] = data.get("path_with_namespace") or data.get("name")
        return data
    # endregion create_gitlab_repository

    # region create_gitlab_merge_request [TYPE Function]
    # @PURPOSE: Create merge request in GitLab.
    # @PRE  Config and remote URL are valid.
    # @POST  Returns normalized MR metadata.
    # @RETURN  Dict[str, Any]
    async def create_gitlab_merge_request(
        self, server_url: str, pat: str, remote_url: str, from_branch: str, to_branch: str,
        title: str, description: str | None = None, remove_source_branch: bool = False,
    ) -> dict[str, Any]:
        identity = self._parse_remote_repo_identity(remote_url)
        base_url = self._normalize_git_server_url(server_url)
        project_id = quote(identity["full_name"], safe="")
        api_url = f"{base_url}/api/v4/projects/{project_id}/merge_requests"
        headers = {"PRIVATE-TOKEN": pat.strip(), "Content-Type": "application/json", "Accept": "application/json"}
        payload = {
            "source_branch": from_branch, "target_branch": to_branch, "title": title,
            "description": description or "", "remove_source_branch": bool(remove_source_branch),
        }
        try:
            response = await self._http_client.post(api_url, headers=headers, json=payload)
        except Exception as e:
            raise HTTPException(status_code=503, detail=f"GitLab API is unavailable: {e!s}")
        if response.status_code >= 400:
            detail = response.text
            try:
                parsed = response.json()
                if isinstance(parsed, dict):
                    detail = parsed.get("message") or detail
            except Exception:
                pass
            raise HTTPException(status_code=response.status_code, detail=f"GitLab API error: {detail}")
        data = response.json()
        return {"id": data.get("iid") or data.get("id"), "url": data.get("web_url") or data.get("url"), "status": data.get("state") or "opened"}
    # endregion create_gitlab_merge_request
# #endregion GitServiceGitlabMixin
# #endregion GitServiceRemoteMixin
