import json
import logging

import boto3
from botocore.exceptions import ClientError

from edpconfigprovider import ConfigProvider
from edpconfigprovider.exceptions import NoConfigurationException


logger = logging.getLogger(__name__)


class AwsSsmProvider(ConfigProvider):

    _CLIENT = None

    def __init__(
        self,
        parameter_path: str = '/',
        recursive: bool = True,
        split_path: bool = True,
        decrypt: bool = True,
    ):
        self.parameter_path = parameter_path
        self.recursive = recursive
        self.split_path = split_path
        self.decrypt = decrypt

    @classmethod
    def get_ssm_client(cls):
        if cls._CLIENT is None:
            cls._CLIENT = boto3.client("ssm")
        return cls._CLIENT

    def get_configuration(self) -> dict:
        try:
            client = self.get_ssm_client()
            paginator = client.get_paginator("get_parameters_by_path")
            pages = paginator.paginate(
                Path=self.parameter_path,
                Recursive=self.recursive,
                WithDecryption=self.decrypt,
            )

            configuration = {}
            for page in pages:
                for parameter in page["Parameters"]:
                    name = parameter["Name"]
                    key = name[len(self.parameter_path):].lstrip("/")
                    value = parameter["Value"]

                    if parameter.get("Type") == "StringList":
                        value = value.split(",")
                    else:
                        try:
                            value = json.loads(value)
                        except (json.JSONDecodeError, TypeError):
                            pass
                    if self.split_path:
                        parts = key.split("/")
                        target = configuration
                        for part in parts[:-1]:
                            existing = target.get(part)
                            if isinstance(existing, dict):
                                target = existing
                            else:
                                if existing is not None:
                                    logger.warning(
                                        "SSM parameter '%s/%s' scalar value will be"
                                        " overwritten by a nested dict (from key '%s')",
                                        self.parameter_path.rstrip("/"),
                                        part,
                                        name,
                                    )
                                target[part] = {}
                                target = target[part]
                        target[parts[-1]] = value
                    else:
                        configuration[key] = value

            if not configuration:
                raise NoConfigurationException(
                    configuration_name=self.parameter_path
                )

            return configuration

        except ClientError:
            raise NoConfigurationException(
                configuration_name=self.parameter_path
            )
