from mongoengine.queryset import Q

from app.models.brand import Brand
from app.services.mapper_service import IDMapper, ExistChecker

class BrandService:
    """
    Service class for creating and retrieving brands.

    Methods:
        create_brand(data: dict) -> dict:
            Creates a new brand and returns its data as a dictionary.

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

        get_brand_by_id(brand_id: str) -> dict:
            Retrieves a single brand by its ID and returns its data as a dictionary.

        update_brand(brand_id: str, data: dict) -> dict:
            Updates an existing brand and returns its updated data as a dictionary.

        delete_brand(brand_id: str) -> bool:
            Deletes a brand by its ID and returns True if successful, False otherwise.

        search_brands(query: str) -> list[dict]:
            Searches for brands based on a query string in 'name' or other fields.

        update_brand_field(brand_id: str, field_name: str, new_value) -> dict:
            Update a specific field of an existing brand by its ID.

        brand_exists(name: str) -> bool:
            Check if a brand with a specific name exists in the database.
    """

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

        Args:
            data (dict): A dictionary containing brand data.

        Returns:
            dict: The created brand data as a dictionary.

        Example:
            # Example data for creating a brand
            brand_data = {
                'name': 'Example Brand',
                'description': 'Brand Description',
                # Add other brand fields here
            }

            # Create a new brand using the BrandService
            new_brand_data = BrandService.create_brand(brand_data)
        """
        # Create a new Brand object with the provided data
        brand = Brand(**data)
        # Save the newly created brand to the database
        brand.save()
        # Convert the brand object to a dictionary representation
        return brand.to_dict()

    @staticmethod
    def get_all_brands() -> list:
        """
        Retrieve all brands from the database.

        Returns:
            List: A list of all brand data as dictionaries.

        Example:
            all_brands_data = BrandService.get_all_brands()
        """
        # Query the database to retrieve all brand objects
        brands = Brand.objects()
        # Convert each brand object to a dictionary representation and create a list of them
        brand_data_list = [brand.to_dict() for brand in brands]
        # Return the list of brand data as dictionaries
        return brand_data_list

    @staticmethod
    def get_brand_by_id(brand_id: str) -> dict:
        """
        Retrieve a single brand by its ID.

        Args:
            brand_id (str): The ID of the brand to retrieve.

        Returns:
            dict: The brand data as a dictionary.

        Example:
            brand_data = BrandService.get_brand_by_id('12345')
        """
        # Map the public ID to the internal ID for the Brand model
        brand_id = IDMapper.public_id_mapper(brand_id, Brand)
        # Check if the mapping was successful and if an internal ID exists
        if not brand_id:
            return None
        # Query the database to retrieve the brand by its internal ID
        brand = Brand.objects(id=brand_id).first()
        # Convert the brand object to a dictionary representation
        return brand

    @staticmethod
    def update_brand(brand_id: str, data: dict) -> dict:
        """
        Update an existing brand by its ID.

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

        Returns:
            dict: The updated brand data as a dictionary.

        Example:
            updated_brand_data = BrandService.update_brand('12345', {'name': 'Updated Brand Name', 'description': 'Updated Description'})
        """
        # Retrieve the existing brand data by its ID using the get_brand_by_id function
        brand = BrandService.get_brand_by_id(brand_id)

        # Check if the brand exists
        if brand:
            # Update the brand fields with the provided data
            brand.modify(**data)
            # Save the updated brand data
            brand.update()
            # Convert the updated brand object to a dictionary representation
            return brand.to_dict()
        else:
            # Return None or raise an exception if the brand with the specified ID doesn't exist.
            return None

    @staticmethod
    def delete_brand(brand_id: str) -> bool:
        """
        Delete a brand by its ID.

        Args:
            brand_id (str): The ID of the brand to delete.

        Returns:
            bool: True if the brand was successfully deleted, False otherwise.

        Example:
            result = BrandService.delete_brand('12345')
        """
        # Retrieve the existing brand data by its ID using the get_brand_by_id function
        brand = BrandService.get_brand_by_id(brand_id)
        print("Brand: ",brand)
        # Check if the brand exists
        if brand:
            # Delete the brand from the database
            brand.delete()
            # Return True to indicate a successful deletion
            return True
        else:
            # Return False if the brand with the specified ID doesn't exist.
            return False

    @staticmethod
    def search_brands(query: str) -> list:
        """
        Search for brands based on a query string in 'name' or other fields.

        Args:
            query (str): The search query.

        Returns:
            List: A list of matching brand data as dictionaries.

        Example:
            matching_brands = BrandService.search_brands('example')
        """
        # Create a query to search in 'name' or other relevant fields
        brands = Brand.objects(Q(name__icontains=query) | Q(description__icontains=query))
        # Convert the matching brand objects to a list of dictionaries
        return [brand.to_dict() for brand in brands]

    @staticmethod
    def update_brand_field(brand_id: str, field_name: str, new_value) -> dict:
        """
        Update a specific field of an existing brand by its ID.

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

        Returns:
            dict: The updated brand data as a dictionary.

        Example:
            updated_brand_data = BrandService.update_brand_field('12345', 'name', 'Updated Brand Name')
        """
        # Retrieve the existing brand data by its ID using the get_brand_by_id function
        brand = BrandService.get_brand_by_id(brand_id)

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

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

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

        Returns:
            bool: True if the brand exists, False otherwise.

        Example:
            exists = BrandService.brand_exists('example')
        """
        # Query the database to check if a brand with the specified name exists
        return ExistChecker.exist(name, Brand)