#  Copyright (C) 2026
#  ABM, Moscow
#
#  UNPUBLISHED PROPRIETARY MATERIAL.
#  ALL RIGHTS RESERVED.
#
#  Authors: Mike Orlov <m.orlov@abm-jsc.ru>
import importlib
import inspect
import os
from dataclasses import dataclass
from typing import Callable, Any, Awaitable, ClassVar, Self

Getter = Callable[[str], Any | Awaitable[Any]]


@dataclass(frozen=True, slots=True)
class ParsedAbmUrl:
    protocol: str
    path_and_args: str
    postprocessor: str
    PROTOCOL_SEPARATOR: ClassVar[str] = "://"
    POSTPROCESSOR_SEPARATOR: ClassVar[str] = "|"

    @classmethod
    def get_getter_by_protocol(cls, protocol: str, **kwargs) -> Getter:
        if protocol == "kwargs":
            return kwargs.get
        if protocol == "import":
            return importlib.import_module
        if protocol == "envvar":
            return os.environ.get
        if protocol in ('http', 'https'):
            raise NotImplementedError(f"Not yet supported protocol: {protocol}")
        if protocol == 'none':
            return lambda x: None
        raise ValueError(f"Unknown protocol: {protocol}")

    @classmethod
    def from_str(cls, abm_url: str) -> Self:
        protocol_end = abm_url.find(cls.PROTOCOL_SEPARATOR)
        protocol = abm_url[:protocol_end].lower()
        path_and_args_end = abm_url.find(cls.POSTPROCESSOR_SEPARATOR, protocol_end)
        if path_and_args_end == -1:
            path_and_args = abm_url[protocol_end+len(cls.PROTOCOL_SEPARATOR):]
            postprocessor = ''
        else:
            path_and_args = abm_url[protocol_end+len(cls.PROTOCOL_SEPARATOR):path_and_args_end]
            postprocessor = abm_url[path_and_args_end+len(cls.POSTPROCESSOR_SEPARATOR):]
        if cls.POSTPROCESSOR_SEPARATOR in postprocessor:
            raise ValueError(f"Only one postprocessor allowed, got: {cls.POSTPROCESSOR_SEPARATOR + postprocessor}")
        return cls(protocol, path_and_args, postprocessor)

    async def get(self, **kwargs) -> Any:
        connector = self.get_getter_by_protocol(self.protocol, **kwargs)
        try:
            result = connector(self.path_and_args)
            if inspect.isawaitable(result):
                result = await result
        except Exception:
            return None
        if self.postprocessor:
            postprocessor = eval("lambda _: " + self.postprocessor)
            try:
                result = postprocessor(result)
            except Exception:
                return None
        return result
