from __future__ import annotations

from typing import TYPE_CHECKING

from django.http import HttpRequest

from allauth.headless.account.response import email_address_data
from allauth.headless.adapter import get_adapter
from allauth.headless.base.response import APIResponse
from allauth.headless.constants import Client, Flow
from allauth.socialaccount.adapter import get_adapter as get_socialaccount_adapter
from allauth.socialaccount.internal.flows import signup
from allauth.socialaccount.providers.oauth2.provider import OAuth2Provider


if TYPE_CHECKING:
    from allauth.socialaccount.models import SocialLogin


def _socialaccount_data(request: HttpRequest, account) -> dict:
    return {
        "uid": account.uid,
        "provider": _provider_data(request, account.get_provider()),
        "display": account.get_provider_account().to_str(),
    }


def _provider_data(request: HttpRequest, provider) -> dict:
    ret = {"id": provider.sub_id, "name": provider.name, "flows": []}
    if provider.supports_redirect:
        ret["flows"].append(Flow.PROVIDER_REDIRECT)
    if provider.supports_token_authentication:
        ret["flows"].append(Flow.PROVIDER_TOKEN)
    if isinstance(provider, OAuth2Provider):
        ret["client_id"] = provider.app.client_id
        if provider.id == "openid_connect":
            ret["openid_configuration_url"] = provider.server_url  # type: ignore[attr-defined]

    return ret


def provider_flows(request: HttpRequest) -> list:
    flows = []
    providers = _list_supported_providers(request)
    if providers:
        redirect_providers = [p.sub_id for p in providers if p.supports_redirect]
        token_providers = [
            p.sub_id for p in providers if p.supports_token_authentication
        ]
        if redirect_providers and request.allauth.headless.client == Client.BROWSER:  # type: ignore[attr-defined]
            flows.append(
                {
                    "id": Flow.PROVIDER_REDIRECT,
                    "providers": redirect_providers,
                }
            )
        if token_providers:
            flows.append(
                {
                    "id": Flow.PROVIDER_TOKEN,
                    "providers": token_providers,
                }
            )
        sociallogin = signup.get_pending_signup(request)
        if sociallogin:
            flows.append(_signup_flow(request, sociallogin))
    return flows


def _signup_flow(request: HttpRequest, sociallogin: SocialLogin) -> dict:
    provider = sociallogin.provider
    flow = {
        "id": Flow.PROVIDER_SIGNUP,
        "provider": _provider_data(request, provider),
        "is_pending": True,
    }
    return flow


def _is_provider_supported(provider, client) -> bool:
    if client == Client.APP:
        return provider.supports_token_authentication
    elif client == Client.BROWSER:
        return provider.supports_redirect
    return False


def _list_supported_providers(request: HttpRequest) -> list:
    adapter = get_socialaccount_adapter()
    providers = adapter.list_providers(request)
    providers = [
        p
        for p in providers
        if _is_provider_supported(p, request.allauth.headless.client)  # type: ignore[attr-defined]
    ]
    return providers


def get_config_data(request: HttpRequest) -> dict:
    entries: list[dict] = []
    data = {"socialaccount": {"providers": entries}}
    providers = _list_supported_providers(request)
    providers = sorted(providers, key=lambda p: p.name)
    for provider in providers:
        entries.append(_provider_data(request, provider))
    return data


class SocialAccountsResponse(APIResponse):
    def __init__(self, request: HttpRequest, accounts) -> None:
        data = [_socialaccount_data(request, account) for account in accounts]
        super().__init__(request, data=data)


class SocialLoginResponse(APIResponse):
    def __init__(self, request: HttpRequest, sociallogin: SocialLogin) -> None:
        adapter = get_adapter()
        assert sociallogin.user  # nosec
        data = {
            "user": adapter.serialize_user(sociallogin.user),
            "account": _socialaccount_data(request, sociallogin.account),
            "email": [email_address_data(ea) for ea in sociallogin.email_addresses],
        }
        super().__init__(request, data=data)
