"""
Welcome to Youtube Autonomous Project Editor
Module.

Module to work as a whole project video editor
with all the capabilities we have been 
programming all this time.
"""
from yta_project_editor.timeline_layer import TimelineLayer
from yta_project_editor.validator import ProjectEditorValidator
from yta_project_editor.settings import DEFAULT_DIMENSIONS
from yta_video_base.duration import set_video_duration
from yta_constants.video import ExtendVideoMode
from yta_constants.project import TimelineLayerType
from yta_validation.parameter import ParameterValidator
from yta_validation import PythonValidator
from moviepy import CompositeVideoClip
from typing import Union


class ProjectEditor:
    """
    Class representing a whole but single video project
    in which we have a timeline with different layers
    and clips on them.
    """

    @property
    def start_time(
        self
    ) -> float:
        """
        The moment in which the first timeline layer
        starts.
        """
        return (
            min(
                self.timeline_layers,
                key = lambda timeline_layer: timeline_layer.start_time
            ).start_time
            if self.has_timeline_layers else
            0.0
        )
    
    @property
    def end_time(
        self
    ) -> float:
        """
        The moment in which the last timeline layer
        finished.
        """
        return (
            max(
                self.timeline_layers,
                key = lambda timeline_layer: timeline_layer.end_time
            ).end_time
            if self.has_timeline_layers else
            0.0
        )

    @property
    def duration(
        self
    ) -> float:
        """
        The duration of the project based on all the
        timeline layers that are set.
        """
        return self.end_time - self.start_time
         
    @property
    def has_timeline_layers(
        self
    ) -> bool:
        """
        Boolean to indicate if it has timeline layers
        or not.
        """
        return len(self.timeline_layers) > 0

    def __init__(
        self,
        dimensions: tuple[int, int] = (DEFAULT_DIMENSIONS[0], DEFAULT_DIMENSIONS[1])
    ):
        ProjectEditorValidator.validate_dimensions(dimensions)

        self.timeline_layers: list[TimelineLayer] = [
            TimelineLayer(0, TimelineLayerType.VIDEO),
            # TODO: I Simplify everything by now and I only
            # handle one video layer
            #TimelineLayer(0, TimelineLayerType.AUDIO)
        ]
        """
        The different timeline layers that hold the
        video, audio and other elements of the 
        project.
        """
        self.dimensions: tuple[int, int] = dimensions
        """
        Dimensions of the final video that has to be
        exported.
        """

    def get_layers(
        self,
        type: TimelineLayerType
    ) -> list[TimelineLayer]:
        """
        Get the timeline layers of the provided 'type' sorted by
        index in ascending order.
        """
        type = TimelineLayerType.to_enum(type)

        return sorted(
            [
                layer
                for layer in self.timeline_layers
                if layer.type == type
            ], 
            key = lambda layer: layer.index
        )
    
    def get_last_layer(
        self,
        type: TimelineLayerType
    ) -> Union[TimelineLayer, None]:
        """
        Get the last layer of the type provided,
        which means the one that ends the last.
        """
        filtered = self.get_layers(type)

        return (
            max(filtered, key = lambda l: l.end_time)
            if filtered else
            None
        )
    
    # TODO: I think this is useless
    def get_last_layer_index(
        self,
        type: TimelineLayerType
    ) -> Union[int, None]:
        """
        Get the last index used for the layers of the given
        'type', that will be None if no layers of that type.
        """
        type = TimelineLayerType.to_enum(type)

        layers = self.get_layers(type)

        # TODO: Be careful, this is the index within the
        # list of that type, not within the general list
        return (
            layers[-1:].index
            if not PythonValidator.is_empty_list(layers) else
            None
        )

    def add_layer(
        self,
        type: TimelineLayerType = TimelineLayerType.VIDEO
    ) -> int:
        """
        Add a new layer of the provided 'type' and returns
        the index in which it has been placed.
        """
        type = (
            TimelineLayerType.to_enum(type)
            if type is not None else
            TimelineLayerType.VIDEO
        )

        layers = self.get_layers(type)
        index = (
            len(layers) + 1
            if layers else
            0
        )

        self.timeline_layers.append(TimelineLayer(index, type))

        return index
    
    def remove_layer(
        self,
        layer: TimelineLayer
    ):
        ParameterValidator.validate_mandatory_instance_of('layer', layer, TimelineLayer)
        
        if layer not in self.timeline_layers:
            raise Exception('The provided "layer" does not exist in this project.')

        self.timeline_layers.remove(layer)

    def build(
        self
    ):
        # TODO: Remove this code below when this is done 
        # individually by each layer. By now I'm forcing
        # all layers clips to have the same duration as
        # the longest layer clip has, but must be done
        # using a general and common 'max_duration'
        # property that is not calculated here.
        layers_clips = [
            layer.build()
            for layer in self.timeline_layers
        ]
        max_duration = max(
            layer_clip.duration
            for layer_clip in layers_clips
        )
        layers_clips = [
            set_video_duration(layer_clip, max_duration, extend_mode = ExtendVideoMode.BLACK_TRANSPARENT_BACKGROUND)
            for layer_clip in layers_clips
        ]

        return CompositeVideoClip(layers_clips)