import asyncio

from integrations.store_repo_utils import store_repositories_in_db
from pydantic import SecretStr
from server.auth.token_manager import TokenManager

from openhands.app_server.integrations.github.github_service import GitHubService
from openhands.app_server.integrations.service_types import ProviderType, Repository
from openhands.app_server.types import AppMode
from openhands.app_server.utils.logger import openhands_logger as logger


class SaaSGitHubService(GitHubService):
    def __init__(
        self,
        user_id: str | None = None,
        external_auth_token: SecretStr | None = None,
        external_auth_id: str | None = None,
        token: SecretStr | None = None,
        external_token_manager: bool = False,
        base_domain: str | None = None,
    ):
        logger.debug(
            f'SaaSGitHubService created with user_id {user_id}, external_auth_id {external_auth_id}, external_auth_token {'set' if external_auth_token else 'None'}, github_token {'set' if token else 'None'}, external_token_manager {external_token_manager}'
        )
        super().__init__(
            user_id=user_id,
            external_auth_token=external_auth_token,
            external_auth_id=external_auth_id,
            token=token,
            external_token_manager=external_token_manager,
            base_domain=base_domain,
        )

        self.external_auth_token = external_auth_token
        self.external_auth_id = external_auth_id
        self.token_manager = TokenManager(external=external_token_manager)

    async def get_latest_token(self) -> SecretStr | None:
        github_token = None
        if self.external_auth_token:
            github_token = SecretStr(
                await self.token_manager.get_idp_token(
                    self.external_auth_token.get_secret_value(), ProviderType.GITHUB
                )
            )
            logger.debug(
                f'Got GitHub token {github_token} from access token: {self.external_auth_token}'
            )
        elif self.external_auth_id:
            offline_token = await self.token_manager.load_offline_token(
                self.external_auth_id
            )
            github_token_str: str | None = (
                await self.token_manager.get_idp_token_from_offline_token(
                    offline_token, ProviderType.GITHUB
                )
                if offline_token
                else None
            )
            github_token = SecretStr(github_token_str) if github_token_str else None
            logger.debug(
                f'Got GitHub token {github_token} from external auth user ID: {self.external_auth_id}'
            )
        elif self.user_id:
            github_token_str = await self.token_manager.get_idp_token_from_idp_user_id(
                self.user_id, ProviderType.GITHUB
            )
            github_token = SecretStr(github_token_str) if github_token_str else None
            logger.debug(
                f'Got GitHub token {github_token} from user ID: {self.user_id}'
            )
        else:
            logger.warning('external_auth_token and user_id not set!')
        return github_token

    async def get_pr_patches(
        self, owner: str, repo: str, pr_number: int, per_page: int = 30, page: int = 1
    ):
        """Get patches for files changed in a PR with pagination support.

        Args:
            owner: Repository owner
            repo: Repository name
            pr_number: Pull request number
            per_page: Number of files per page (default: 30, max: 100)
            page: Page number to fetch (default: 1)
        """
        url = f'https://api.github.com/repos/{owner}/{repo}/pulls/{pr_number}/files'
        params = {'per_page': min(per_page, 100), 'page': page}  # GitHub max is 100
        response, headers = await self._make_request(url, params)

        # Parse pagination info from headers
        has_next_page = 'next' in headers.get('link', '')
        total_count = int(headers.get('total', 0))

        return {
            'files': response,
            'pagination': {
                'has_next_page': has_next_page,
                'total_count': total_count,
                'current_page': page,
                'per_page': per_page,
            },
        }

    async def get_repository_node_id(self, repo_id: str) -> str:
        """
        Get the new GitHub GraphQL node ID for a repository using REST API.

        Args:
            repo_id: Numeric repository ID as string (e.g., "123456789")

        Returns:
            New format node ID for GraphQL queries (e.g., "R_kgDOLfkiww")

        Raises:
            Exception: If the API request fails or node_id is not found
        """
        url = f'https://api.github.com/repositories/{repo_id}'
        response, _ = await self._make_request(url)
        node_id = response.get('node_id')
        if not node_id:
            raise Exception(f'No node_id found for repository {repo_id}')
        return node_id

    async def _get_external_auth_id(self) -> str | None:
        """Get or fetch external_auth_id from Keycloak token if not already set."""
        if self.external_auth_id:
            return self.external_auth_id

        if self.external_auth_token:
            try:
                user_info = await self.token_manager.get_user_info(
                    self.external_auth_token.get_secret_value()
                )
                self.external_auth_id = user_info.sub
                logger.info(
                    f'Determined external_auth_id from Keycloak token: {self.external_auth_id}'
                )
                return self.external_auth_id
            except Exception as e:
                logger.warning(
                    f'Could not determine external_auth_id from token: {e}',
                    exc_info=True,
                )
        return None

    async def get_paginated_repos(self, page, per_page, sort, installation_id):
        repositories = await super().get_paginated_repos(
            page, per_page, sort, installation_id
        )
        external_auth_id = await self._get_external_auth_id()
        if external_auth_id:
            asyncio.create_task(
                store_repositories_in_db(repositories, external_auth_id)
            )
        return repositories

    async def get_all_repositories(
        self, sort: str, app_mode: AppMode
    ) -> list[Repository]:
        repositories = await super().get_all_repositories(sort, app_mode)
        # Schedule the background task without awaiting it
        external_auth_id = await self._get_external_auth_id()
        if external_auth_id:
            asyncio.create_task(
                store_repositories_in_db(repositories, external_auth_id)
            )
        # Return repositories immediately
        return repositories
