from base64 import b64encode
from typing import Optional
from urllib.parse import urlencode

from backend.data.model import OAuth2Credentials
from backend.integrations.providers import ProviderName
from backend.util.request import Requests

from .base import BaseOAuthHandler


class NotionOAuthHandler(BaseOAuthHandler):
    """
    Based on the documentation at https://developers.notion.com/docs/authorization

    Notes:
    - Notion uses non-expiring access tokens and therefore doesn't have a refresh flow
    - Notion doesn't use scopes
    """

    PROVIDER_NAME = ProviderName.NOTION

    def __init__(self, client_id: str, client_secret: str, redirect_uri: str):
        self.client_id = client_id
        self.client_secret = client_secret
        self.redirect_uri = redirect_uri
        self.auth_base_url = "https://api.notion.com/v1/oauth/authorize"
        self.token_url = "https://api.notion.com/v1/oauth/token"

    def get_login_url(
        self, scopes: list[str], state: str, code_challenge: Optional[str]
    ) -> str:
        params = {
            "client_id": self.client_id,
            "redirect_uri": self.redirect_uri,
            "response_type": "code",
            "owner": "user",
            "state": state,
        }
        return f"{self.auth_base_url}?{urlencode(params)}"

    async def exchange_code_for_tokens(
        self, code: str, scopes: list[str], code_verifier: Optional[str]
    ) -> OAuth2Credentials:
        request_body = {
            "grant_type": "authorization_code",
            "code": code,
            "redirect_uri": self.redirect_uri,
        }
        auth_str = b64encode(f"{self.client_id}:{self.client_secret}".encode()).decode()
        headers = {
            "Authorization": f"Basic {auth_str}",
            "Accept": "application/json",
        }
        response = await Requests().post(
            self.token_url, json=request_body, headers=headers
        )
        token_data = response.json()
        # Email is only available for non-bot users
        email = (
            token_data["owner"]["person"]["email"]
            if "person" in token_data["owner"]
            and "email" in token_data["owner"]["person"]
            else None
        )

        return OAuth2Credentials(
            provider=self.PROVIDER_NAME,
            title=token_data.get("workspace_name"),
            username=email,
            access_token=token_data["access_token"],
            refresh_token=None,
            access_token_expires_at=None,  # Notion tokens don't expire
            refresh_token_expires_at=None,
            scopes=[],
            metadata={
                "owner": token_data["owner"],
                "bot_id": token_data["bot_id"],
                "workspace_id": token_data["workspace_id"],
                "workspace_name": token_data.get("workspace_name"),
                "workspace_icon": token_data.get("workspace_icon"),
            },
        )

    async def revoke_tokens(self, credentials: OAuth2Credentials) -> bool:
        # Notion doesn't support token revocation
        return False

    async def _refresh_tokens(
        self, credentials: OAuth2Credentials
    ) -> OAuth2Credentials:
        # Notion doesn't support token refresh
        return credentials

    def needs_refresh(self, credentials: OAuth2Credentials) -> bool:
        # Notion access tokens don't expire
        return False
