import importlib
import os
from typing import (
    Dict,
    Optional,
    Sequence,
)

from messagebus import MessageBus
from model.exceptions import (
    ErrorBuildingDataBuilder,
    ReportNotImplemented,
)
from nreports.repository import (
    I18N,
    OrderAPIRepository,
    StoreConfigRepository,
    UserRepository,
)

from ._DataBuilder import DataBuilder


class DataBuilderFactory(object):

    def __init__(
        self,
        message_bus: MessageBus,
        order_api_repository: OrderAPIRepository,
        store_config_repository: StoreConfigRepository,
        i18n_repository: I18N,
        extra_modules: Optional[Sequence[str]],
        images_path: Dict[str, str],
        constants: Dict[str, str],
        user_repository: UserRepository,
    ) -> None:

        self.message_bus = message_bus
        self.order_api_repository = order_api_repository
        self.store_config_repository = store_config_repository
        self.store_config_repository = store_config_repository
        self.user_repository = user_repository
        self.message_bus = message_bus
        self.extra_modules = extra_modules or []
        self.i18n_repository = i18n_repository
        self.images_path = images_path
        self.constants = constants

        self.all_modules = ["nreports.builders"] + list(self.extra_modules)

    def build(
        self,
        report_name: str,
    ) -> DataBuilder:

        self._append_modules_recursively()

        for module_path in self.all_modules:
            try:
                module = importlib.import_module(module_path)
                class_name = self._to_pascal_case(snake_str=report_name) + "Builder"

                data_builder = getattr(module, class_name)(
                    self.message_bus,
                    self.order_api_repository,
                    self.store_config_repository,
                    self.i18n_repository,
                    self.images_path,
                    self.constants,
                    self.user_repository,
                )
                return data_builder

            except (AttributeError, ModuleNotFoundError):
                pass

            except Exception as ex:
                raise ErrorBuildingDataBuilder(
                    report_name=report_name,
                    error_message=str(ex),
                )

        raise ReportNotImplemented(report_name=report_name)

    def _append_modules_recursively(
        self,
    ) -> None:

        current_dir = os.path.dirname(__file__)
        for root, dirs, files in os.walk(current_dir):
            for folder in dirs:
                if folder == "__pycache__":
                    continue

                if os.path.split(root)[1] == "builders":
                    self.all_modules.append(f"nreports.builders.{folder}")
                else:
                    builders_folders = os.path.abspath(root).split("builders\\")
                    if len(builders_folders) < 2:
                        continue

                    module_full_path = builders_folders[1].replace("\\", ".")
                    self.all_modules.append(f"nreports.builders.{module_full_path}.{folder}")

    @staticmethod
    def _to_pascal_case(
        snake_str: str
    ) -> str:

        components = snake_str.split("_")
        return "".join(x.lower().title() for x in components)
