import gradio as gr
import yaml
import os

bfh_blue = gr.themes.utils.colors.Color(
    c50="#edf0f2",
    c100="#dbe0e5",
    c200="#b7c1cb",
    c300="#93a2b1",
    c400="#6f8397",
    c500="#4b647d",
    c600="#3c5064",
    c700="#2d3c4b",
    c800="#1e2832",
    c900="#0f1419",
    c950="#070a0c",
    name="bfh_blue",
)

bfh_yellow = gr.themes.colors.Color(
    c50="#fff9e6",
    c100="#fef3cc",
    c200="#fde799",
    c300="#fcdb66",
    c400="#fbcf33",
    c500="#fac300",
    c600="#c89c00",
    c700="#967500",
    c800="#644e00",
    c900="#322700",
    c950="#191300",
    name="bfh_yellow",
)


class BFHTheme(gr.themes.Base):
    def __init__(
        self,
    ):
        super().__init__(
            primary_hue=bfh_blue,
            secondary_hue=bfh_yellow,
        )


class WrappedGradioObject:
    def __init__(self, title, gradio_element):
        self.title = title
        self.gradio_element = gradio_element

    def launch(self):
        return self.gradio_element.launch()

    @staticmethod
    def read_yaml(path):
        with open(path) as f:
            return yaml.safe_load(f)

    @staticmethod
    def read_file(path):
        with open(path) as f:
            return f.read()


class GradioInterfaceWrapper(WrappedGradioObject):
    @classmethod
    def from_yaml(cls, path):
        """Initializes Interface from YAML file."""
        content_dict = cls.read_yaml(path)
        return cls.create_interface(**content_dict)

    @classmethod
    def create_interface(cls, name, title, description, examples=None):
        """Creates Gradio-Element containing an interface."""
        description = cls._prepend_link_to_description(name, title, description)
        interface = gr.load(
            name,
            title=None,  # Having the Tab-Name is sufficient.
            description=description,
            examples=examples,
        )
        return cls(title, interface)

    @staticmethod
    def _prepend_link_to_description(name, title, description):
        without_huggingface = name.removeprefix("huggingface/")
        link = f"https://huggingface.co/{without_huggingface}"
        return f'<a href="{link}">{title}</a> </br> {description}'


class GradioTabWrapper(WrappedGradioObject):
    @classmethod
    def from_gradio_object_list(cls, title, gradio_objects):
        """Constructs a GradioTabWrapper from a title and a list of WrappedGradioObjects."""
        interface = gr.TabbedInterface(
            [obj.gradio_element for obj in gradio_objects],
            [obj.title for obj in gradio_objects],
            theme=BFHTheme(),
        )
        return cls(title, interface)

    @classmethod
    def from_yaml(cls, path):
        content_dict = cls.read_yaml(path)
        gradio_objects = [
            cls._read_dependency(dependency)
            for dependency in content_dict["dependencies"]
        ]
        return cls.from_gradio_object_list(
            content_dict["title"],
            gradio_objects,
        )

    @staticmethod
    def _read_dependency(path):
        full_path = f"resources/{path}"
        if path.startswith("interfaces"):
            return GradioInterfaceWrapper.from_yaml(full_path)
        if path.startswith("tabs"):
            return GradioTabWrapper.from_yaml(full_path)
        if path.startswith("markdown"):
            return GradioMarkdownWrapper.from_markdown(full_path)
        raise ValueError(
            "Gradio Object Type could not be inferred from path name. Make sure "
            "that all interface object yamls are in resources/interfaces, "
            "all tab object yamls are in resources/tabs, and all markdown objects "
            "are in resources/markdown."
        )


class GradioMarkdownWrapper(WrappedGradioObject):
    @classmethod
    def from_markdown(cls, path):
        """Creates text from markdown. The title is inferred from the file name."""
        content = cls.read_file(path)
        title = cls.title_from_path(path)
        return cls(title, gr.Markdown(content))

    @staticmethod
    def title_from_path(path):
        _, last = os.path.split(path)
        name, _ = os.path.splitext(last)
        return name.capitalize()