# Copyright (c) 2026 MergeGuide, Inc. All rights reserved.
# SPDX-License-Identifier: Proprietary
"""Azure DevOps authentication handling.

Azure DevOps supports Personal Access Tokens (PAT) for API authentication.
PATs are passed via HTTP Basic Auth with an empty username.

Example:
    auth = AzureDevOpsAuth(
        org_url="https://dev.azure.com/myorg",
        pat="xxxxxxxxxxxxx"
    )
    headers = auth.get_auth_headers()
    # {"Authorization": "Basic OjxwYXQ+"}
"""

from __future__ import annotations

import base64
from dataclasses import dataclass
from urllib.parse import urlparse


@dataclass
class AzureDevOpsAuth:
    """Authentication handler for Azure DevOps API.

    Uses Personal Access Token (PAT) authentication via HTTP Basic Auth.
    The username is empty and the password is the PAT.

    Attributes:
        org_url: Azure DevOps organization URL (e.g., https://dev.azure.com/myorg)
        pat: Personal Access Token with required scopes

    Required PAT Scopes:
        - Code (Read): Access repository content
        - Code (Status): Post build statuses
        - Pull Request Threads (Read & Write): Post comments

    Example:
        >>> auth = AzureDevOpsAuth(
        ...     org_url="https://dev.azure.com/myorg",
        ...     pat="xxxxxxxxxxxxxxxx"
        ... )
        >>> headers = auth.get_auth_headers()
        >>> # Use headers for API requests
    """

    org_url: str
    pat: str

    def __post_init__(self) -> None:
        """Validate and normalize the organization URL."""
        # Remove trailing slash
        self.org_url = self.org_url.rstrip("/")

        # Validate URL format
        parsed = urlparse(self.org_url)
        if not parsed.scheme or not parsed.netloc:
            raise ValueError(f"Invalid organization URL: {self.org_url}")

        # Validate PAT is not empty
        if not self.pat or not self.pat.strip():
            raise ValueError("Personal Access Token cannot be empty")

    def get_auth_headers(self) -> dict[str, str]:
        """Return HTTP headers for authenticated requests.

        Azure DevOps uses Basic Auth with an empty username and PAT as password.
        The format is: `Authorization: Basic base64(":PAT")`

        Returns:
            Dictionary with Authorization header.
        """
        # Azure DevOps Basic Auth: empty username, PAT as password
        credentials = f":{self.pat}"
        encoded = base64.b64encode(credentials.encode()).decode()
        return {"Authorization": f"Basic {encoded}"}

    def get_api_base_url(self, project: str | None = None) -> str:
        """Get the base URL for API requests.

        Args:
            project: Optional project name/ID to include in URL.

        Returns:
            Base URL for API endpoints.

        Example:
            >>> auth.get_api_base_url()
            "https://dev.azure.com/myorg/_apis"
            >>> auth.get_api_base_url("MyProject")
            "https://dev.azure.com/myorg/MyProject/_apis"
        """
        if project:
            return f"{self.org_url}/{project}/_apis"
        return f"{self.org_url}/_apis"

    @property
    def organization(self) -> str:
        """Extract organization name from URL.

        Returns:
            Organization name extracted from the URL path.

        Example:
            >>> auth = AzureDevOpsAuth("https://dev.azure.com/myorg", "pat")
            >>> auth.organization
            "myorg"
        """
        parsed = urlparse(self.org_url)
        # URL format: https://dev.azure.com/{org}
        path_parts = parsed.path.strip("/").split("/")
        if path_parts:
            return path_parts[0]
        return ""

    def validate_token(self) -> bool:
        """Check if the PAT appears to be valid format.

        This only checks format, not actual validity with Azure DevOps.
        Use test_connection() for actual validation.

        Returns:
            True if PAT format looks valid.
        """
        # Azure DevOps PATs are typically 52 characters
        # but can vary, so just check it's non-empty and reasonable length
        return len(self.pat) >= 20

    def __repr__(self) -> str:
        """Return string representation without exposing PAT."""
        return f"AzureDevOpsAuth(org_url={self.org_url!r}, pat=****)"
