# Copyright (C) 2023 Cochise Ruhulessin
#
# All rights reserved. No warranty, explicit or implicit, provided. In
# no event shall the author(s) be liable for any claim or damages.
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
import importlib
import urllib.parse
from typing import cast

from canonical import HTTPResourceLocator
from canonical import ResourceName

from .types import ICryptoBackend
from .versionedcryptokey import VersionedCryptoKey


_BACKENDS: list[ICryptoBackend] = []


def install_backend(qualname: str, _version: str = 'v1') -> None:
    """Given the qualified name to a Python module, install
    the backend implementation of cryptographic operations.
    """
    module = importlib.import_module(qualname)
    if module not in _BACKENDS: # type: ignore
        _BACKENDS.insert(0, module) # type: ignore


def get_backend(uri: str | urllib.parse.ParseResult) -> ICryptoBackend:
    install_backend('aiopki.ext.cryptography')
    if not isinstance(uri, urllib.parse.ParseResult):
        uri = urllib.parse.urlparse(uri)
    backend = None
    for candidate in _BACKENDS:
        if not candidate.handles(uri):
            continue
        backend = candidate
        break
    if backend is None:
        raise ValueError("Unable to determine backend for URI.")
    return backend


def parse_uri(uri: str):
    backend = get_backend(uri)
    return backend.parse_key(urllib.parse.urlparse(uri))


def parse(name: HTTPResourceLocator | ResourceName | str) -> VersionedCryptoKey:
    obj = None
    for candidate in _BACKENDS:
        obj = candidate.parse(name)
        if obj is not None:
            break
    if obj is None:
        raise ValueError(f"Unknown key type: {name}")
    return cast(VersionedCryptoKey, obj)