#
#   Copyright © 2022 Evilham
#
#   This file is part of DD
#
#   DD is free software: you can redistribute it and/or modify
#   it under the terms of the GNU Affero General Public License as published by
#   the Free Software Foundation, either version 3 of the License, or (at your
#   option) any later version.
#
#   DD is distributed in the hope that it will be useful, but WITHOUT ANY
#   WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
#   FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
#   details.
#
#   You should have received a copy of the GNU Affero General Public License
#   along with DD. If not, see <https://www.gnu.org/licenses/>.
#
# SPDX-License-Identifier: AGPL-3.0-or-later

import logging as log
import os
import time
import traceback
from typing import Any, Dict

from admin.lib.keycloak_client import KeycloakClient

try:
    # Python 3.9+
    from functools import cache  # type: ignore  # Currently targetting 3.8
except ImportError:
    # Up to python 3.8
    from functools import cached_property as cache


class SamlService(object):
    """
    Generic class to manage a SAML service on keycloak.

    This is currently only used by Email and Nextcloud, but the plan is to
    migrate other *_saml.py scripts to use this.
    """

    keycloak: KeycloakClient
    client_name: str
    domain: str = os.environ["DOMAIN"]

    def __init__(self, client_name : str):
        self.keycloak = KeycloakClient()
        self.client_name = client_name

    @cache
    def public_cert(self) -> str:
        """
        Read the public SAML certificate as used by keycloak
        """
        ready = False
        basepath = os.path.dirname(__file__)
        crt = ""
        while not ready:
            # TODO: Check why they were using a loop
            try:
                with open(
                    os.path.abspath(
                        os.path.join(basepath, f"../saml_certs/{self.client_name}.crt")
                    ),
                    "r",
                ) as crtf:
                    crt = crtf.read()
                    ready = True
            except IOError:
                log.warning(f"Could not get public certificate for {self.client_name} SAML. Retrying...")
                time.sleep(2)
            except:
                log.error(traceback.format_exc())
        log.info("Got public SAML certificate")
        return crt

    def wait_for_database(self) -> None:
        """
        Helper function to wait for a database to be up.
        """
        ready = False
        while not ready:
            try:
                self.connect_to_db()
                ready = True
            except:
                log.warning("Could not connect to {self.client_name} database. Retrying...")
                time.sleep(2)
        log.info("Connected to {self.client_name} database.")

    def connect_to_db(self) -> None:
        """
        This should be overriden in subclasses.
        """
        pass

    def get_client(self, client_id: str) -> Any:
        # TODO: merge with keycloak_config.py
        self.keycloak.connect()
        k = self.keycloak.keycloak_admin

        clients = k.get_clients()
        client = next(filter(lambda c: c["clientId"] == client_id, clients), None)
        return (k, client)

    def set_client(self, client_id: str, client_overrides: Dict[str, Any]) -> str:
        (k, client) = self.get_client(client_id)
        if client is None:
            client_uid = k.create_client(client_overrides)
        else:
            client_uid = client["id"]
            k.update_client(client_uid, client_overrides)
        return client_id

    def configure(self) -> None:
        pass
