from app.config.extensions import db

from app.models.genre import Genre
from app.models.program import Program
from app.services.mapper_service import IDMapper, ExistChecker

class ProgramService:
    """
    Service class for creating and retrieving TV programs.

    Methods:
        create_program(data: dict) -> dict:
            Creates a new TV program and returns its data as a dictionary.

        get_all_programs() -> list[dict]:
            Retrieves all TV programs from the database and returns their data as a list of dictionaries.

        get_program_by_id(program_id: str) -> dict:
            Retrieves a single TV program by its ID and returns its data as a dictionary.

        update_program(program_id: str, data: dict) -> dict:
            Updates an existing TV program and returns its updated data as a dictionary.

        delete_program(program_id: str) -> bool:
            Deletes a TV program by its ID and returns True if successful, False otherwise.

        search_programs(query: str) -> list[dict]:
            Searches for TV programs based on a query string in 'name' or 'summary' fields.

        update_program_field(program_id: str, field_name: str, new_value) -> dict:
            Update a specific field of an existing TV program by its ID.

        get_programs_by_genre(genre_id: str) -> list:
            Retrieve all TV programs within a specific genre.

        count_programs() -> int:
            Count the total number of TV programs in the database.

        delete_all_programs() -> int:
            Delete all TV programs in the database.

        program_exists(name: str) -> bool:
            Check if a TV program with a specific name exists in the database.
    """

    @staticmethod
    def create_program(data: dict) -> dict:
        """
        Create a new TV program and save it to the database.

        Args:
            data (dict): A dictionary containing TV program data.

        Returns:
            dict: The created TV program data as a dictionary.
        """
        # Create a new Program object with the provided data
        program = Program(**data)

        # save new genre

        # genres = [genre for genre in program['genre']]
        # Save the newly created program to the database
        # print("Genre: ",genres)
        # program.genre.genre=genres
        program.save()
        # # Convert the program object to a dictionary representation
        return program.to_dict()
        
    @staticmethod
    def get_all_programs() -> list:
        """
        Retrieve all TV programs from the database.

        Returns:
            List: A list of all TV program data as dictionaries.
        """
        # Query the database to retrieve all program objects
        programs = Program.objects()
        # Convert each program object to a dictionary representation and create a list of them
        program_data_list = [program.to_dict() for program in programs]
        # Return the list of program data as dictionaries
        return program_data_list
    
    @staticmethod
    def get_program_by_id(program_id: str) -> dict:
        """
        Retrieve a single TV program by its ID.

        Args:
            program_id (str): The ID of the program to retrieve.

        Returns:
            dict: The program data as a dictionary.
        """
        # Map the public ID to the internal ID for the Program model
        program_id = IDMapper.public_id_mapper(program_id, Program)
        # Check if the mapping was successful and if an internal ID exists
        if not program_id:
            return None
        # Query the database to retrieve the program by its internal ID
        program = Program.objects(id=program_id).first()
        # Convert the program object to a dictionary representation
        return program.to_dict()
    
    @staticmethod
    def update_program(program_id: str, data: dict) -> dict:
        """
        Update an existing TV program by its ID.

        Args:
            program_id (str): The ID of the program to update.
            data (dict): A dictionary containing the updated program data.

        Returns:
            dict: The updated program data as a dictionary.
        """
        # Retrieve the existing program data by its ID using the get_program_by_id function
        program = ProgramService.get_program_by_id(program_id)

        # Check if the program exists
        if program:
            # Update the program fields with the provided data
            program.modify(**data)
            # Save the updated program data
            program.update()
            # Convert the updated program object to a dictionary representation
            return program 
        else:
            # Return None or raise an exception if the program with the specified ID doesn't exist.
            return None
        
    @staticmethod
    def delete_program(program_id: str) -> bool:
        """
        Delete a TV program by its ID.

        Args:
            program_id (str): The ID of the program to delete.

        Returns:
            bool: True if the program was successfully deleted, False otherwise.
        """
        # Retrieve the existing program data by its ID using the get_program_by_id function
        program = ProgramService.get_program_by_id(program_id)

        # Check if the program exists
        if program:
            # Delete the program from the database
            program.delete()
            # Return True to indicate a successful deletion
            return True
        else:
            # Return False if the program with the specified ID doesn't exist.
            return False
        
    @staticmethod
    def search_programs(query: str) -> list:
        """
        Search for TV programs based on a query string in 'name' or 'summary' fields.

        Args:
            query (str): The search query.

        Returns:
            List: A list of matching TV program data as dictionaries.
        """
        # Create a query to search in 'name' and 'summary' fields
        programs = Program.objects(
            db.Q(name__icontains=query) | db.Q(summary__icontains=query)
        )

        # Convert the matching program objects to a list of dictionaries
        return [program.to_dict() for program in programs]
    
    @staticmethod
    def update_program_field(program_id: str, field_name: str, new_value) -> dict:
        """
        Update a specific field of an existing TV program by its ID.

        Args:
            program_id (str): The ID of the program to update.
            field_name (str): The name of the field to update.
            new_value: The new value for the field.

        Returns:
            dict: The updated program data as a dictionary.
        """
        # Retrieve the existing program data by its ID using the get_program_by_id function
        program = ProgramService.get_program_by_id(program_id)

        # Check if the program exists
        if program:
            # Update the specific field with the new value
            setattr(program, field_name, new_value)
            program.save()  # Save the changes to the database
            # Convert the updated program object to a dictionary representation
            return program.to_dict()
        else:
            # Return None or raise an exception if the program with the specified ID doesn't exist.
            return None

    @staticmethod
    def get_programs_by_genre(genre_id: str) -> list:
        """
        Retrieve all TV programs within a specific genre.

        Args:
            genre_id (str): The ID of the genre to filter programs by.

        Returns:
            List: A list of TV program data as dictionaries within the specified genre.
        """
        # Map the public genre ID to the internal ID for the Genre model
        genre_id = IDMapper.public_id_mapper(genre_id, Genre)
        # Check if the mapping was successful and if an internal genre ID exists
        if not genre_id:
            return []
        # Query the database to retrieve programs in the specified genre
        programs = Program.objects(genre=genre_id)
        # Convert the matching program objects to a list of dictionaries
        return [program.to_dict() for program in programs]

    @staticmethod
    def count_programs() -> int:
        """
        Count the total number of TV programs in the database.

        Returns:
            int: The total count of TV programs.
        """
        # Use the .count() method to count the number of programs in the database
        return Program.objects().count()

    @staticmethod
    def delete_all_programs() -> int:
        """
        Delete all TV programs in the database.

        Returns:
            int: The number of TV programs deleted.
        """
        # Query the database to retrieve all TV programs
        programs = Program.objects()
        # Initialize a count for deleted programs
        deleted_count = 0
        # Iterate through the list of TV programs and delete each one
        for program in programs:
            # Delete the program from the database
            program.delete()
            deleted_count += 1
        # Return the total count of deleted TV programs
        return deleted_count

    @staticmethod
    def program_exists(name: str) -> bool:
        """
        Check if a TV program with a specific name exists in the database.

        Args:
            name (str): The name of the program to check.

        Returns:
            bool: True if the program exists, False otherwise.
        """
        # Query the database to check if a program with the specified name exists
        return ExistChecker.exist(name, Program)


    @staticmethod
    def get_programs_by_language(language: str) -> list:
        """
        Retrieve all TV programs with a specific language.

        Args:
            language (str): The language to filter programs by.

        Returns:
            List: A list of TV program data as dictionaries with the specified language.
        """
        # Query the database to retrieve programs in the specified language
        programs = Program.objects(language=language)
        # Convert the matching program objects to a list of dictionaries
        return [program.to_dict() for program in programs]

    @staticmethod
    def get_programs_by_status(status: str) -> list:
        """
        Retrieve all TV programs with a specific status.

        Args:
            status (str): The status to filter programs by.

        Returns:
            List: A list of TV program data as dictionaries with the specified status.
        """
        # Query the database to retrieve programs with the specified status
        programs = Program.objects(status=status)
        # Convert the matching program objects to a list of dictionaries
        return [program.to_dict() for program in programs]

    @staticmethod
    def get_programs_by_type(program_type: str) -> list:
        """
        Retrieve all TV programs with a specific program type.

        Args:
            program_type (str): The program type to filter programs by.

        Returns:
            List: A list of TV program data as dictionaries with the specified program type.
        """
        # Query the database to retrieve programs with the specified program type
        programs = Program.objects(program_type=program_type)
        # Convert the matching program objects to a list of dictionaries
        return [program.to_dict() for program in programs]
 