"""Bitbucket OAuth/API client for customer-owned repositories."""
from __future__ import annotations

import time
import urllib.parse
from typing import Any, Optional

import httpx

from src.config import Settings, get_settings

BITBUCKET_API = "https://api.bitbucket.org/2.0"
BITBUCKET_WEB = "https://bitbucket.org"


class BitbucketUserClient:
    def __init__(self, *, access_token: Optional[str] = None, settings: Optional[Settings] = None):
        self._settings = settings or get_settings()
        self._access_token = (access_token or "").strip()

    def authorization_url(self, *, redirect_uri: str, state: str) -> str:
        params = {
            "client_id": (self._settings.bitbucket_user_oauth_client_id or "").strip(),
            "response_type": "code",
            "state": state,
        }
        return f"{BITBUCKET_WEB}/site/oauth2/authorize?{urllib.parse.urlencode(params)}"

    async def exchange_code(self, *, code: str, redirect_uri: str) -> dict[str, Any]:
        async with httpx.AsyncClient(timeout=60.0) as client:
            r = await client.post(
                f"{BITBUCKET_WEB}/site/oauth2/access_token",
                auth=(
                    self._settings.bitbucket_user_oauth_client_id,
                    self._settings.bitbucket_user_oauth_client_secret,
                ),
                data={
                    "grant_type": "authorization_code",
                    "code": code,
                    "redirect_uri": redirect_uri,
                },
            )
        r.raise_for_status()
        return r.json()

    async def get_current_user(self) -> dict[str, Any]:
        async with httpx.AsyncClient(timeout=60.0) as client:
            r = await client.get(f"{BITBUCKET_API}/user", headers=self._headers())
        r.raise_for_status()
        return r.json()

    async def list_repositories(self) -> list[dict[str, Any]]:
        out: list[dict[str, Any]] = []
        url: Optional[str] = (
            f"{BITBUCKET_API}/repositories?"
            + urllib.parse.urlencode({"role": "member", "pagelen": "100"})
        )
        async with httpx.AsyncClient(timeout=120.0) as client:
            while url:
                r = await client.get(url, headers=self._headers())
                r.raise_for_status()
                data = r.json()
                out.extend(data.get("values") or [])
                url = data.get("next")
        return out

    async def get_repository(self, full_name: str) -> dict[str, Any]:
        workspace, slug = split_bitbucket_full_name(full_name)
        async with httpx.AsyncClient(timeout=60.0) as client:
            r = await client.get(
                f"{BITBUCKET_API}/repositories/{workspace}/{slug}",
                headers=self._headers(),
            )
        r.raise_for_status()
        return r.json()

    def authenticated_clone_url(self, repo_full_name: str) -> str:
        path = repo_full_name.strip().strip("/")
        token = urllib.parse.quote(self._access_token, safe="")
        return f"https://x-token-auth:{token}@bitbucket.org/{path}.git"

    async def register_repo_webhook(
        self, repo_full_name: str, webhook_url: str, secret: str
    ) -> str:
        """Register a webhook on a Bitbucket repo. Returns the webhook UUID as a string."""
        workspace, slug = split_bitbucket_full_name(repo_full_name)
        async with httpx.AsyncClient(timeout=30.0) as client:
            r = await client.post(
                f"{BITBUCKET_API}/repositories/{workspace}/{slug}/hooks",
                headers=self._headers(),
                json={
                    "description": "P2HS mirror",
                    "url": webhook_url,
                    "active": True,
                    "secret": secret,
                    "events": [
                        "repo:push",
                        "repo:updated",
                        "repo:transfer",
                        "repo:deleted",
                    ],
                },
            )
        r.raise_for_status()
        raw_uuid = r.json().get("uuid") or ""
        return raw_uuid.strip("{}")

    async def delete_repo_webhook(self, repo_full_name: str, webhook_uuid: str) -> None:
        """Delete a previously registered webhook. Silently ignores 404 (already gone)."""
        workspace, slug = split_bitbucket_full_name(repo_full_name)
        uid = webhook_uuid.strip("{}")
        async with httpx.AsyncClient(timeout=30.0) as client:
            r = await client.delete(
                f"{BITBUCKET_API}/repositories/{workspace}/{slug}/hooks/{{{uid}}}",
                headers=self._headers(),
            )
        if r.status_code == 404:
            return
        r.raise_for_status()

    def token_expiry_epoch(self, token_payload: dict[str, Any]) -> Optional[float]:
        expires_in = token_payload.get("expires_in")
        try:
            return time.time() + int(expires_in)
        except Exception:
            return None

    def _headers(self) -> dict[str, str]:
        return {"Authorization": f"Bearer {self._access_token}"}


def split_bitbucket_full_name(full_name: str) -> tuple[str, str]:
    raw = (full_name or "").strip().strip("/")
    parts = raw.split("/")
    if len(parts) != 2 or not parts[0] or not parts[1]:
        raise ValueError("Bitbucket repository must be in workspace/repo_slug form.")
    return parts[0], parts[1]
