import logging
import time
from pathlib import Path
from typing import Optional

from pydantic import BaseSettings
from pydantic import SecretStr
from pydantic import validator

from click.types import LazyFile

from bulk_restore_tool.models import Device


class UTCFormatter(logging.Formatter):
    converter = time.gmtime


brt_format = UTCFormatter(
    fmt="%(asctime)s %(levelname)s %(name)s - %(message)s", datefmt="%Y-%m-%dT%H:%M:%SZ"
)


class BRTSettings(BaseSettings):
    """
    Setting configuration class for the Bulk Restore Tool.

    Inheriting from [pydantic.BaseSettings](https://pydantic-docs.helpmanual.io/usage/settings/) enables environment
    variable and .env file fallbacks if values are not passed in during instantiation, allowing flexible configuration
    from multiple possible sources, which-ever is easier for the end-user.
    """

    api_client_id: str
    api_client_secret: SecretStr
    url: str
    log_file: str = "bulk_restore.log"
    log_level: str = "INFO"
    _logger: Optional[logging.Logger] = None
    _device_loggers: Optional[dict] = None

    def __init__(self, **kwargs):
        # clear any keys from kwargs that are passed as None, which allows fallback to values in ENV
        kwargs = {k: v for k, v in kwargs.items() if v is not None}
        super().__init__(**kwargs)

    @validator("log_file")
    def validate_log_file(cls, value):  # noqa
        if "~" in value:
            value = str(value.replace("~", str(Path.home())))

        # will raise if not valid for writing but won't create file
        LazyFile(value, "a")

        return value

    @property
    def logger(self):
        if self._logger is None:
            handler = logging.FileHandler(
                filename=self.log_file,
            )
            handler.setFormatter(brt_format)
            self._logger = logging.getLogger("main")
            self._logger.setLevel(self.log_level)
            self._logger.addHandler(handler)
        return self._logger

    def get_device_loggers(self, device: Device, target: Path):
        if self._device_loggers is None:
            self._device_loggers = {}

        if device.deviceId not in self._device_loggers:
            device_logger = logging.getLogger(device.deviceId)
            device_filename = f"{device.username}_{device.name}_{device.deviceId}.log"
            device_handler = logging.FileHandler(filename=target / device_filename)
            device_handler.setFormatter(brt_format)
            device_logger.addHandler(device_handler)
            device_logger.handlers.extend(self.logger.handlers)
            device_logger.setLevel("INFO")

            error_logger = logging.getLogger(f"{device.deviceId}-error")
            error_filename = (
                f"{device.username}_{device.name}_{device.deviceId}_error.log"
            )
            error_handler = logging.FileHandler(filename=target / error_filename)
            error_handler.setFormatter(brt_format)
            error_logger.addHandler(error_handler)
            error_logger.handlers.extend(self.logger.handlers)
            error_logger.setLevel("INFO")

            self._device_loggers[device.deviceId] = (device_logger, error_logger)

        return self._device_loggers[device.deviceId]

    class Config:
        underscore_attrs_are_private = True
        env_prefix = "brt_"
        env_file = ".env"
