# Copyright (C) 2020  The Software Heritage developers
# See the AUTHORS file at the top-level directory of this distribution
# License: GNU Affero General Public License version 3, or any later version
# See top-level LICENSE file for more information

from copy import copy
from unittest.mock import Mock

from django.utils import timezone

from swh.web.auth.keycloak import KeycloakOpenIDConnect
from swh.web.auth.utils import OIDC_SWH_WEB_CLIENT_ID
from swh.web.config import get_config

from .sample_data import oidc_profile, realm_public_key, userinfo


class KeycloackOpenIDConnectMock(KeycloakOpenIDConnect):
    def __init__(self, auth_success=True, exp=None):
        swhweb_config = get_config()
        super().__init__(
            swhweb_config["keycloak"]["server_url"],
            swhweb_config["keycloak"]["realm_name"],
            OIDC_SWH_WEB_CLIENT_ID,
        )
        self.auth_success = auth_success
        self.exp = exp
        self._keycloak.public_key = lambda: realm_public_key
        self._keycloak.well_know = lambda: {
            "issuer": f"{self.server_url}realms/{self.realm_name}",
            "authorization_endpoint": (
                f"{self.server_url}realms/"
                f"{self.realm_name}/protocol/"
                "openid-connect/auth"
            ),
            "token_endpoint": (
                f"{self.server_url}realms/{self.realm_name}/"
                "protocol/openid-connect/token"
            ),
            "token_introspection_endpoint": (
                f"{self.server_url}realms/"
                f"{self.realm_name}/protocol/"
                "openid-connect/token/"
                "introspect"
            ),
            "userinfo_endpoint": (
                f"{self.server_url}realms/{self.realm_name}/"
                "protocol/openid-connect/userinfo"
            ),
            "end_session_endpoint": (
                f"{self.server_url}realms/"
                f"{self.realm_name}/protocol/"
                "openid-connect/logout"
            ),
            "jwks_uri": (
                f"{self.server_url}realms/{self.realm_name}/"
                "protocol/openid-connect/certs"
            ),
        }
        self.authorization_code = Mock()
        self.userinfo = Mock()
        self.logout = Mock()
        if auth_success:
            self.authorization_code.return_value = copy(oidc_profile)
            self.userinfo.return_value = copy(userinfo)
        else:
            self.authorization_url = Mock()
            exception = Exception("Authentication failed")
            self.authorization_code.side_effect = exception
            self.authorization_url.side_effect = exception
            self.userinfo.side_effect = exception
            self.logout.side_effect = exception

    def decode_token(self, token):
        options = {}
        if self.auth_success:
            # skip signature expiration check as we use a static oidc_profile
            # for the tests with expired tokens in it
            options["verify_exp"] = False
        decoded = super().decode_token(token, options)
        # tweak auth and exp time for tests
        expire_in = decoded["exp"] - decoded["auth_time"]
        if self.exp is not None:
            decoded["exp"] = self.exp
            decoded["auth_time"] = self.exp - expire_in
        else:
            decoded["auth_time"] = int(timezone.now().timestamp())
            decoded["exp"] = decoded["auth_time"] + expire_in
        decoded["groups"] = ["/staff"]
        return decoded


def mock_keycloak(mocker, auth_success=True, exp=None):
    kc_oidc_mock = KeycloackOpenIDConnectMock(auth_success, exp)
    mock_get_oidc_client = mocker.patch("swh.web.auth.views.get_oidc_client")
    mock_get_oidc_client.return_value = kc_oidc_mock
    mocker.patch("swh.web.auth.backends._oidc_client", kc_oidc_mock)
    return kc_oidc_mock
