from youtube_autonomous.elements.validator.element_parameter_validator import ElementParameterValidator, ParameterValidator
from youtube_autonomous.segments.enums import SegmentType, EnhancementType
from youtube_autonomous.segments.builder.ai import create_ai_narration
from yta_general_utils.programming.parameter_obtainer import ParameterObtainer
from yta_general_utils.programming.parameter_validator import PythonValidator
from yta_general_utils.file.enums import FileTypeX
from yta_general_utils.programming.output import Output
from typing import Union


class ElementBuilder:
    # TODO: Maybe __init__ that returns the corresponding subclass
    # according to the provided 'type' (?)
    # https://stackoverflow.com/questions/9187388/possible-to-prevent-init-from-being-called

    @staticmethod
    def get_subclasses():
        from youtube_autonomous.elements.builder.ai_image_element_builder import AIImageElementBuilder
        from youtube_autonomous.elements.builder.image_element_builder import ImageElementBuilder
        from youtube_autonomous.elements.builder.ai_video_element_builder import AIVideoElementBuilder
        from youtube_autonomous.elements.builder.video_element_builder import VideoElementBuilder
        from youtube_autonomous.elements.builder.custom_stock_element_builder import CustomStockElementBuilder
        from youtube_autonomous.elements.builder.stock_element_builder import StockElementBuilder
        from youtube_autonomous.elements.builder.meme_element_builder import MemeElementBuilder
        from youtube_autonomous.elements.builder.sound_element_builder import SoundElementBuilder
        from youtube_autonomous.elements.builder.youtube_video_element_builder import YoutubeVideoElementBuilder
        from youtube_autonomous.elements.builder.text_element_builder import TextElementBuilder
        from youtube_autonomous.elements.builder.premade_element_builder import PremadeElementBuilder
        from youtube_autonomous.elements.builder.effect_element_builder import EffectElementBuilder
        from youtube_autonomous.elements.builder.greenscreen_element_builder import GreenscreenElementBuilder

        return [
            AIImageElementBuilder,
            AIVideoElementBuilder,
            ImageElementBuilder,
            VideoElementBuilder,
            CustomStockElementBuilder,
            StockElementBuilder,
            MemeElementBuilder,
            SoundElementBuilder,
            YoutubeVideoElementBuilder,
            TextElementBuilder,
            PremadeElementBuilder,
            EffectElementBuilder,
            GreenscreenElementBuilder
        ]

    @staticmethod
    def get_subclasses_as_str():
        return ', '.join(ElementBuilder.get_subclasses())
    
    @staticmethod
    def get_subclass_by_type(type: Union[SegmentType, EnhancementType, str]):
        type = ElementParameterValidator.validate_segment_or_enhancement_type(type)

        from youtube_autonomous.elements.builder.ai_image_element_builder import AIImageElementBuilder
        from youtube_autonomous.elements.builder.image_element_builder import ImageElementBuilder
        from youtube_autonomous.elements.builder.ai_video_element_builder import AIVideoElementBuilder
        from youtube_autonomous.elements.builder.video_element_builder import VideoElementBuilder
        from youtube_autonomous.elements.builder.custom_stock_element_builder import CustomStockElementBuilder
        from youtube_autonomous.elements.builder.stock_element_builder import StockElementBuilder
        from youtube_autonomous.elements.builder.meme_element_builder import MemeElementBuilder
        from youtube_autonomous.elements.builder.sound_element_builder import SoundElementBuilder
        from youtube_autonomous.elements.builder.youtube_video_element_builder import YoutubeVideoElementBuilder
        from youtube_autonomous.elements.builder.text_element_builder import TextElementBuilder
        from youtube_autonomous.elements.builder.premade_element_builder import PremadeElementBuilder
        from youtube_autonomous.elements.builder.effect_element_builder import EffectElementBuilder
        from youtube_autonomous.elements.builder.greenscreen_element_builder import GreenscreenElementBuilder

        return {
            SegmentType.MEME: MemeElementBuilder,
            EnhancementType.MEME: MemeElementBuilder,
            SegmentType.AI_IMAGE: AIImageElementBuilder, 
            EnhancementType.AI_IMAGE: AIImageElementBuilder,
            SegmentType.AI_VIDEO: AIVideoElementBuilder,
            EnhancementType.AI_VIDEO: AIVideoElementBuilder,
            SegmentType.IMAGE: ImageElementBuilder,
            EnhancementType.IMAGE: ImageElementBuilder,
            SegmentType.VIDEO: VideoElementBuilder,
            EnhancementType.VIDEO: VideoElementBuilder,
            SegmentType.STOCK: StockElementBuilder,
            EnhancementType.STOCK: StockElementBuilder,
            SegmentType.CUSTOM_STOCK: CustomStockElementBuilder,
            EnhancementType.CUSTOM_STOCK: CustomStockElementBuilder,
            SegmentType.SOUND: SoundElementBuilder,
            EnhancementType.SOUND: SoundElementBuilder,
            SegmentType.YOUTUBE_VIDEO: YoutubeVideoElementBuilder,
            EnhancementType.YOUTUBE_VIDEO: YoutubeVideoElementBuilder,
            SegmentType.TEXT: TextElementBuilder,
            EnhancementType.TEXT: TextElementBuilder,
            SegmentType.PREMADE: PremadeElementBuilder,
            EnhancementType.PREMADE: PremadeElementBuilder,
            EnhancementType.EFFECT: EffectElementBuilder,
            EnhancementType.GREENSCREEN: GreenscreenElementBuilder
        }[type]
    
    @classmethod
    def build_narration(
        cls,
        text: str,
        output_filename: Union[str, None]
    ):
        """
        Generates a narration file that narrates the 'text' provided and
        is stored locally as 'output_filename'. If 'text' or 
        'output_filename' fields are not provided it will raise an 
        Exception.
        """
        ParameterValidator.validate_string_mandatory_parameter('text', text)

        return create_ai_narration(
            text,
            output_filename = Output.get_filename(output_filename, FileTypeX.AUDIO)
        )

    @classmethod
    def get_parameters_from_method(
        # TODO: We should force 'element' to be a dict
        cls,
        method: callable,
        element: dict,
        parameters_to_ignore: list[str] = [],
        parameters_strictly_from_element: list[str] = [],
        parameters_strictly_from_extra_data: list[str] = []
    ) -> dict:
        """
        Extract the parameters from the given 'method' that are
        available in the also provided 'element' and 'extra_data',
        ignoring the ones in the 'parameters_to_ignore' list and
        applying the strategy that the other 2 'strictly' 
        parameters indicate.

        This method will return all the parameters that have been
        found only if all the mandatory ones were found, or will
        raise an Exception if some mandatory parameter is missing.

        The next parameters will be always ignored: 'self', 'cls',
        'args', 'kwargs'.

        The return value is dict in which the keys are the
        parameters names and the values, the values of those
        parameters.

        Params:
            parameters_to_ignore: The parameters we want to get
            ignored when obtained from the 'method' signature.

        Returns: 
            A dict in which the keys are the parameters names and
            the values are the values of those parameters.
        """
        if not PythonValidator.is_dict(element):
            raise Exception('The provided "element" parameter is not a dict.')
        
        if not PythonValidator.is_callable(method):
            raise Exception('The provided "method" is not callable.')
        
        if not PythonValidator.is_list_of_string(parameters_to_ignore):
            raise Exception('The provided "parameters_to_ignore" is not a list of strings.')

        if not PythonValidator.is_list_of_string(parameters_strictly_from_element):
            raise Exception('The provided "parameters_strictly_from_element" is not a list of strings.')
        
        if not PythonValidator.is_list_of_string(parameters_strictly_from_extra_data):
            raise Exception('The provided "parameters_strictly_from_extra_data" is not a list of strings.')
        
        # If keys are duplicated in both 'strictly' arrays we
        # keep the one in 'from_element'
        parameters_strictly_from_extra_data = [
            parameter
            for parameter in parameters_strictly_from_extra_data
            if parameter not in parameters_strictly_from_element
        ]

        # We will force these normal parameters to ignore them
        parameters_to_ignore += ['self', 'cls', 'args', 'kwargs']

        # We obtain the 'extra_data'
        extra_data = element.get('extra_data', {})

        # 1. Get the parameters we need from the method
        parameters_we_need = ParameterObtainer.get_parameters_from_methodX(method)

        # 2. We remove the parameters we want to ignore
        for name in parameters_to_ignore:
            parameters_we_need.remove(name)

        # 3. Lets obtain the real parameters
        parameters = {}

        # 4. Get parameter values from 'extra_data''
        # Include the parameters found in 'extra_data'
        parameters.update({
            name: value
            for name, value in extra_data.items()
            if (
                parameters_we_need.has(name) and
                name not in parameters_strictly_from_element
            )
        })

        # 4. Get parameter values from the 'element' and replace
        # the ones extracted from 'extra_data' if needed
        parameters.update({
            name: value
            for name, value in element.items()
            if (
                parameters_we_need.has(name) and
                name not in parameters_strictly_from_extra_data
            )
        })

        # 5. Raise exception if some mandatory param is missing
        missing_keys = set(parameters_we_need.mandatory.as_dict.keys()) - set(parameters.keys())

        if len(missing_keys) > 0:
            print('We have found this parameters:')
            print(parameters)
            raise Exception(f'Some required parameters are missing: {", ".join(missing_keys)}')
        
        # TODO: Maybe rebuild as Parameters to return (?)
        return parameters
    
    @classmethod
    def handle_narration_from_segment(cls, segment: dict):
        pass