from youtube_autonomous.database.database_handler import ProjectDatabaseHandler
from youtube_autonomous.experimental.segments.validation.segment_validator import SegmentValidator
from youtube_autonomous.project.project import Project
from youtube_autonomous.elements.validator.element_parameter_validator import ElementParameterValidator
from youtube_autonomous.segments.builder.config import DEFAULT_SEGMENT_PARTS_FOLDER, DEFAULT_PROJECTS_OUTPUT_FOLDER
from youtube_autonomous.dataclasses import ProjectAsJson, MongoDBProject
from yta_general_utils.temp import Temp
from yta_general_utils.programming.path import get_project_abspath
from yta_general_utils.path import create_file_abspath
from yta_general_utils.file.reader import FileReader
from yta_general_utils.logger import print_completed, print_in_progress
from yta_general_utils.programming.parameter_validator import PythonValidator
from typing import Union


class YoutubeAutonomous:
    """
    The amazing Youtube Autonomous class that is
    able to magically create and edit youtube
    videos by using AI and other functionalities.
    """

    _database_handler: ProjectDatabaseHandler = None
    """
    Object to interact with the database and get and create projects.

    _This parameter is not manually set by the user._
    """
    __segment_validator: SegmentValidator = None
    """
    Object to validate the segments we want to use to build a project.

    _This parameter is not manually set by the user._
    """
    _segments_abspath: Union[str, None] = None
    """
    The absolute path that points to the segments folder in which this
    software will store the segments related content parts that are
    created during the whole segment building process. This folder 
    allows us recovering a non-finished project in which we generated
    some segments in the past but not all of them.

    _This parameter is not manually set by the user._
    """
    _projects_output_abspath: Union[str, None] = None
    """
    The absolute path that points to the projects output folder in which
    this software will store the final videos generated.

    _This parameter is not manually set by the user._
    """

    @property
    def unfinished_projects(self) -> list[MongoDBProject]:
        """
        Get a list with the unfinished projects from
        the database. This will be empty if there are
        no unfinished projects.
        """
        return [
            MongoDBProject(project.json)
            for project in self._database_handler.unfinished_projects
        ]

    def __init__(self):
        """
        Initializes the object and creates the segment part building
        folder in which segment parts will be stored.
        """
        self._database_handler = ProjectDatabaseHandler()
        # TODO: This is not being used here
        self.__segment_validator = SegmentValidator()

        self._segments_abspath = f'{get_project_abspath()}{DEFAULT_SEGMENT_PARTS_FOLDER}/'
        self._projects_output_abspath = f'{get_project_abspath()}{DEFAULT_PROJECTS_OUTPUT_FOLDER}/'

        # We force to create the folder if it doesn't exist
        create_file_abspath(f'{self._segments_abspath}toforce')
        create_file_abspath(f'{self._projects_output_abspath}toforce')

    def purge(
        self,
        do_remove_segments_files: bool = False
    ):
        """
        Cleans the temporary folder removing all previous generated 
        temporary files, and also the segment files if the
        'do_remove_segments_files' parameter is set to True.
        """
        if not PythonValidator.is_boolean(do_remove_segments_files):
            raise Exception('The provided "do_remove_segments_files" parameter is not a boolean.')

        Temp.clean_folder()

        if do_remove_segments_files:
            # TODO: Remove all files in self._segments_abspath folder
            pass

    def check_config(self):
        # TODO: Check that he config is ok
        pass
     
    def insert_project_in_database_from_file(
        self,
        filename: str
    ) -> str:
        """
        Read the provided project content 'filename' and
        create a new project in the database if the provided
        'filename' contains a new project and is valid. If
        the information belongs to an already registered
        project, it will raise an exception.

        This method returns the new stored project mongo
        ObjectId as a string if successfully stored, or
        raises an Exception if anything went wrong.
        """
        ElementParameterValidator.validate_filename(filename, None)
        
        json_project: ProjectAsJson = self._read_project_from_file(filename)

        # We don't want to have duplicated projects
        if self._database_handler.get_project_by_script(json_project):
            raise Exception('There is an existing project in the database with the same content.')

        # The data has been validated when reading
        db_project = self._database_handler.insert_project(json_project.for_database)

        print_completed('Project created in database with ObjectId = "' + str(db_project.json['_id']) + '"')

        return str(db_project.json['_id'])
    
    def process_unfinished_projects(self) -> bool:
        """
        Get all the unfinished projects and processes them,
        one by one, to build the final video file.
        """
        for db_project in self.unfinished_projects:
            print_in_progress(f'Processing project "{db_project.id}"')
            self._process_project(db_project)
            print_completed(f'Project "{db_project.id}" processed succesfully!')

        return True

    def _process_project(
        self,
        db_project: MongoDBProject
    ) -> bool:
        """
        Process the provided 'db_project' project, which must
        be a valid project stored in the database.
        """
        filename = f'{self._projects_output_abspath}project_{db_project.id}.mp4'

        return Project(db_project.id).build(filename)
    
    def _read_project_from_file(
        self,
        filename: str
    ) -> ProjectAsJson:
        """
        Reads the provided 'filename', that should contain the information of
        a project, and validates its content and structure. This method will
        return the data read as a dict or will raise an Exception if something
        is not ok.
        """
        ElementParameterValidator.validate_filename(filename)

        return ProjectAsJson(FileReader.read_json(filename))