import pathlib

import aiofiles
import pydantic
from fluent.runtime import FluentBundle, FluentResource
from fluent.runtime.types import FluentNone


class FluentProvider:
    """
    Implementation of the protocol for the fluent format,
    does not support data recording and modification!
    """

    @classmethod
    async def from_file(cls, path: pathlib.Path, locale_code: str) -> FluentProvider:
        """
        Allows you to quickly create a new class instance and immediately load the localisation.
        :param path: Path
        :param locale_code: Localisation code
        :return: New instance
        """

        instance = cls()
        await instance.load_file(path, locale_code)
        return instance

    def __init__(self, use_isolating: bool = False) -> None:
        self._storage: FluentModel | None = None
        self.use_isolating = use_isolating

    def __getitem__(self, key: str) -> str | None | FluentNone:
        return self.get(key)

    def __setitem__(self, key: str, value: str) -> str:
        return self.set(key, value)

    def get(self, key: str, default: str | None = None, **kwargs) -> str | None | FluentNone:
        """
        Returns the value by key, formats if kwargs is passed.
        :param key: Key
        :param default: If there is no key, the default will be returned.
        :param kwargs: Formatting arguments (if any).
        :return: Formatted string
        """
        if not self._storage:
            return default

        bundle = self._storage.data

        if not bundle.has_message(key):
            return default

        message = bundle.get_message(key)
        if message.value is None:
            return default

        formatted_string, errors = bundle.format_pattern(message.value, kwargs)
        if errors:
            raise RuntimeError(f"An error occurred while executing format_pattern: {errors}")

        return formatted_string

    def set(self, key: str, value: str) -> str:
        raise NotImplementedError("Fluent provider currently does not support runtime editing.")

    def get_storage(self) -> FluentModel:
        if self._storage is None:
            raise ValueError("The storage is None")
        return self._storage

    def get_locale_code(self) -> str | None:
        return self._storage.locale_code if self._storage else None

    def get_file_path(self) -> pathlib.Path | None:
        return self._storage.path if self._storage else None

    async def load_file(self, path: pathlib.Path, locale_code: str) -> None:
        await self.clean()

        async with aiofiles.open(path, "r", encoding="utf-8") as f:
            content = await f.read()

            resource = FluentResource(content)

            bundle = FluentBundle([locale_code], use_isolating=self.use_isolating)
            bundle.add_resource(resource)

            self._storage = FluentModel(data=bundle, path=path)

    async def save_file(self, path: pathlib.Path | None = None) -> None:
        raise NotImplementedError("Fluent provider currently does not support runtime saving.")

    async def clean(self) -> None:
        self._storage = None

    def is_empty(self) -> bool:
        return self._storage is None


class FluentModel(pydantic.BaseModel):
    model_config = pydantic.ConfigDict(arbitrary_types_allowed=True)

    data: FluentBundle
    path: pathlib.Path | None = None

    version: str = "1.0.0"

    @property
    def locale_code(self) -> str | None:
        if self.data and hasattr(self.data, "locales") and self.data.locales:
            return self.data.locales[0]
        return None
