import json
import logging
from datetime import (
    UTC,
    datetime,
    timedelta,
)

import boto3
from botocore.exceptions import ClientError

from edpconfigprovider import ConfigProvider
from edpconfigprovider.exceptions import NoConfigurationException

logger = logging.getLogger(__name__)


class AwsAppConfigProvider(ConfigProvider):
    def __init__(
        self,
        application_name: str,
        profile_name: str,
        environment_name: str,
    ):
        self.application_name = application_name
        self.profile_name = profile_name
        self.environment_name = environment_name

        self._client = boto3.client("appconfigdata")
        self._configuration_token = None
        self._next_poll_time = None
        self._last_config = None

    def get_configuration(self) -> dict:
        if self._next_poll_time is not None and datetime.now(tz=UTC) < self._next_poll_time:
            return self._last_config

        try:
            self._ensure_session()
            response = self._client.get_latest_configuration(ConfigurationToken=self._configuration_token)
            self._configuration_token = response["NextPollConfigurationToken"]
            self._next_poll_time = datetime.now(tz=UTC) + timedelta(seconds=response["NextPollIntervalInSeconds"])

            new_config = response["Configuration"].read()
            if new_config != b"":
                self._last_config = json.loads(new_config)
            elif self._last_config is None:
                config_name = self._get_config_name()
                logger.error("No configuration found for %s", config_name)
                raise NoConfigurationException(configuration_name=config_name)

            return self._last_config
        except ClientError:
            config_name = self._get_config_name()
            logger.exception("Error getting configuration from AWS AppConfig: %s", config_name)

            self._configuration_token = None
            raise NoConfigurationException(configuration_name=config_name)

    def _ensure_session(self):
        if self._configuration_token is None:
            session = self._client.start_configuration_session(
                ApplicationIdentifier=self.application_name,
                ConfigurationProfileIdentifier=self.profile_name,
                EnvironmentIdentifier=self.environment_name,
                RequiredMinimumPollIntervalInSeconds=15,
            )
            self._configuration_token = session["InitialConfigurationToken"]

    def _get_config_name(self):
        return f"{self.application_name}/{self.environment_name}/{self.profile_name}"
