from pydantic import SecretStr

from openhands.app_server.integrations.azure_devops.azure_devops_service import (
    AzureDevOpsServiceImpl as AzureDevOpsService,
)
from openhands.app_server.integrations.bitbucket.bitbucket_service import (
    BitBucketService,
)
from openhands.app_server.integrations.bitbucket_data_center.bitbucket_dc_service import (
    BitbucketDCService,
)
from openhands.app_server.integrations.forgejo.forgejo_service import ForgejoService
from openhands.app_server.integrations.github.github_service import GitHubService
from openhands.app_server.integrations.gitlab.gitlab_service import GitLabService
from openhands.app_server.integrations.provider import ProviderType
from openhands.app_server.utils.logger import openhands_logger as logger


async def validate_provider_token(
    token: SecretStr, base_domain: str | None = None
) -> ProviderType | None:
    """Determine whether a token is for GitHub, GitLab, Bitbucket, Bitbucket Data Center, or Azure DevOps by attempting to get user info from the services.

    Args:
        token: The token to check
        base_domain: Optional base domain for the service

    Returns:
        'github' if it's a GitHub token
        'gitlab' if it's a GitLab token
        'bitbucket' if it's a Bitbucket token
        'azure_devops' if it's an Azure DevOps token
        None if the token is invalid for all services
    """
    # Skip validation for empty tokens
    if token is None:
        return None  # type: ignore[unreachable]

    # Try GitHub first
    github_error = None
    try:
        github_service = GitHubService(token=token, base_domain=base_domain)
        await github_service.verify_access()
        return ProviderType.GITHUB
    except Exception as e:
        github_error = e

    # Try GitLab next
    gitlab_error = None
    try:
        gitlab_service = GitLabService(token=token, base_domain=base_domain)
        await gitlab_service.get_user()
        return ProviderType.GITLAB
    except Exception as e:
        gitlab_error = e

    # Try Forgejo if a base_domain was provided (custom instances may not contain
    # the substring 'forgejo' or 'codeberg')
    forgejo_error = None
    if base_domain:
        try:
            forgejo_service = ForgejoService(token=token, base_domain=base_domain)
            await forgejo_service.get_user()
            return ProviderType.FORGEJO
        except Exception as e:
            forgejo_error = e

    # Try Bitbucket next
    bitbucket_error = None
    try:
        bitbucket_service = BitBucketService(token=token, base_domain=base_domain)
        await bitbucket_service.get_user()
        return ProviderType.BITBUCKET
    except Exception as e:
        bitbucket_error = e

    # Try Bitbucket Data Center if a base_domain was provided (always self-hosted)
    bitbucket_dc_error = None
    if base_domain:
        try:
            bitbucket_dc_service = BitbucketDCService(
                token=token, base_domain=base_domain
            )
            await bitbucket_dc_service.verify_access()
            return ProviderType.BITBUCKET_DATA_CENTER
        except Exception as e:
            bitbucket_dc_error = e

    # Try Azure DevOps last
    azure_devops_error = None
    try:
        azure_devops_service = AzureDevOpsService(token=token, base_domain=base_domain)
        await azure_devops_service.get_user()
        return ProviderType.AZURE_DEVOPS
    except Exception as e:
        azure_devops_error = e

    logger.debug(
        f'Failed to validate token: {github_error} \n {gitlab_error} \n {forgejo_error} \n {bitbucket_error} \n {bitbucket_dc_error} \n {azure_devops_error}'
    )

    return None
