# Import the necessary components from flask_restx
from flask_restx import Resource, Namespace, fields,Model
# Import the BrandService from the application's services module
from app.services.genre_service import GenreService
# Import the genre_dict and GenreSchema from the application's schemas module
from app.schema.genre import genre_dict, GenreSchema
 

# Create a Flask-RESTx namespace named 'genres' with a description 
genre_namespace = Namespace('genres', path='/genres', description='Genre-related operations')
# Add the genre model to the namespace
genre_model = genre_namespace.model('Genre', {
    'name': fields.String(
        required=True,
        description='The name of the genre. Must be unique.',
        example='Comedy'
    ), 
    'category': fields.String(
        required=True,
        description='The category of the genre (Channel or program).',
        enum=['Channel', 'Program'],
        example='Channel'
    )
    })


@genre_namespace.route('/')
class GenreListResource(Resource):
    """
    Resource for managing a list of genres.

    Attributes:
        None

    Methods:
        - get: Retrieve a list of all genres.
        - post: Create a new genre.
        - delete: Delete all genres.

    Returns:
        - For GET: List of genres.
        - For POST: The newly created genre with HTTP status 201 (Created).
        - For DELETE: Success message with HTTP status 204 (No Content) if genres were deleted,
                      or a message indicating no genres found with HTTP status 404 (Not Found).
    """
    # GET request to retrieve all genres
    @genre_namespace.doc(
        responses={
            200: 'OK - Genres retrieved successfully',
            204: 'No Content - No genres found',
            500: 'Internal Server Error - An error occurred while retrieving genres',
        },
        description='Retrieve a list of all genres.',
        params={
            'page': 'Page number for pagination (default: 1)',
            'limit': 'Number of genres to retrieve per page (default: 20)',
        },
        security='API Key',
        tags=['Genres'],
    )
    def get(self):
        """
        Retrieve a list of all available genres using the GenreService.

        This endpoint returns a list of genres, with optional pagination parameters.

        Responses:
            200:
                description: Genres retrieved successfully.
            204:
                description: No genres found.
            500:
                description: An error occurred while retrieving genres.

        Parameters:
            - page (int, optional): Page number for pagination (default: 1).
            - limit (int, optional): Number of genres to retrieve per page (default: 20).

        Security:
            - API Key

        Tags:
            - Genres

        Returns:
            A dictionary with the following keys:
            - 'msg' (str): A message indicating the success or failure of the request.
            - 'data' (list): A list of genre objects representing various genres.
            - 'status' (int): A custom status code (e.g., 200 for success, 404 for not found, 500 for an error).
        """
        try:
            # Attempt to fetch all genres using the GenreService
            genres = GenreService.get_all_genres()
            if not genres: 
                # No genres found, return a not found response
                return {
                    'msg': 'No genres found',
                    'data': [],
                    'status': 204 
                }, 204 # Not found status code
            # Genres found, return success response
            # Return a dictionary with message, data, and custom status code
            return {
                'msg': 'Genres retrieved successfully',
                'data': genres,
                'code_status': 200 
            }, 200 # OK status code
        except Exception as e:
            if hasattr(e, 'code') and e.code is not None:
                # If the exception has a status code attribute, return it
                return {
                    'msg': str(e),
                    'data': None,
                    'status': e.code   
                }, e.code # Other status code
            else:
                # If no specific status code is provided, return a 500 Internal Server Error
                return {
                    'msg': 'An error occurred while retrieving genres: ' + str(e),
                    'data': None,
                    'status': 500   
                }, 500  # Internal Server Error

    # POST request to create a new genre
    @genre_namespace.expect(genre_model)
    @genre_namespace.doc(
        responses={
            201: 'Created - New genre created successfully',
            400: 'Bad Request - Invalid data',
            409: 'Conflict - Genre with this name already exists',
            500: 'Internal Server Error - An error occurred while creating a new genre',
        },
        description='Create a new genre using the provided data.',
        security='API Key',
        tags=['Genres'],
    )
    def post(self):
        """
        Create a new genre using the provided data.

        This endpoint allows the creation of a new genre by providing JSON data containing genre information.

        Responses:
            201:
                description: New genre created successfully.
            400:
                description: Bad request, invalid data.
            409:
                description: Conflict - Genre with this name already exists.
            500:
                description: An error occurred while creating a new genre.

        Security:
            - API Key

        Tags:
            - Genres

        Parameters:
            - genre_data (JSON, required): JSON data containing genre information (genre_model).

        Request Body:
            - genre_model (object): The data format for creating a new genre.

        Returns:
            A dictionary with the following keys:
            - 'msg' (str): A message indicating the success or failure of the request.
            - 'data' (dict): The newly created genre object.
            - 'status' (int): A custom status code (e.g., 201 for created, 400 for bad request, 409 for conflict, 500 for an error).
        """
        try:
            # Extract the request data (genre information) from the payload
            data = genre_namespace.payload

            # Validate the incoming data using GenreSchema
            genre_schema = GenreSchema()
            errors = genre_schema.validate(data)

            if errors:
                # Return validation errors as a response with a 400 status code
                return {
                    'msg': 'Bad Request - Invalid data',
                    'errors': errors,
                    'data': None,
                    'status': 400
                }, 400
            
            # Check if a genre with the given name already exists
            existing_genre = GenreService.get_genres_by_name(data['name'])
            if existing_genre:
                return {
                    'msg': 'Conflict - Genre with this name already exists',
                    'data': None,
                    'status': 409
                }, 409  # Conflict

            # Create a new genre using the GenreService
            new_genre = GenreService.create_genre(data)
            # Return the newly created genre as the API response with HTTP status 201 (Created)
            return {
                'msg': 'New genre created successfully',
                'data': new_genre.to_dict(),
                'status': 201
            }, 201  # Created status code
        except Exception as e:
            if hasattr(e, 'code') and e.code is not None:
                # If the exception has a status code attribute, return it
                return {
                    'msg': str(e),
                    'data': None,
                    'status': e.code
                }, e.code  # Other status code
            else:
                # If no specific status code is provided, return a 500 Internal Server Error
                return {
                    'msg': 'An error occurred while creating a new genre: ' + str(e),
                    'data': None,
                    'status': 500
                }, 500  # Internal Server Error
    # DELETE request to delete all genres
    @genre_namespace.doc(
        responses={
            204: 'No Content - All genres deleted successfully',
            404: 'Not Found - No genres found',
            500: 'Internal Server Error - An error occurred while deleting genres',
        },
        description='Delete all genres.',
        security='API Key',
        tags=['Genres'],
    )
    def delete(self):
        """
        Delete all genres.

        This endpoint allows the deletion of all genres.

        Responses:
            204:
                description: No Content - All genres deleted successfully.
            404:
                description: Not Found - No genres found.
            500:
                description: Internal Server Error - An error occurred while deleting genres.

        Security:
            - API Key

        Tags:
            - Genres

        Returns:
            A dictionary with the following keys:
            - 'msg' (str): A message indicating the success or failure of the request.
            - 'data' (dict): Additional information or data related to the request.
            - 'status' (int): A custom status code (e.g., 204 for no content, 404 for not found, 500 for an error).
        """
        try:
            # Delete all genres using the GenreService and get the count of deleted genres
            deleted_count = GenreService.delete_all_genres()
            if deleted_count > 0:
                # Return a success message with HTTP status 204 (No Content)
                return {
                    'msg': f'{deleted_count} genres deleted', 
                    'data': None, 
                    'status': 204
                }, 204 # no content
            else:
                # Return a message indicating that no genres were found with HTTP status 404 (Not Found)
                return {
                    'msg': 'No genres found', 
                    'data': None, 
                    'status': 404
                }, 404 # Not Found
        except Exception as e:
            if hasattr(e, 'code') and e.code is not None:
                # If the exception has a status code attribute, return it
                return {
                    'msg': str(e),
                    'data': None,
                    'status': e.code
                }, e.code  # Other status code
            else:
                # If no specific status code is provided, return a 500 Internal Server Error
                return {
                    'msg': 'Internal Server Error - An error occurred while deleting genres: ' + str(e),
                    'data': None,
                    'status': 500
                }, 500  # Internal Server Error
            
@genre_namespace.route('/<string:genre_id>')
class GenreResource(Resource):
    # GET request to retrieve a genre using public id
    @genre_namespace.doc(
        params={
            'genre_id': 'The unique identifier of the genre.'
        },
        responses={
            200: 'OK - Genre retrieved successfully',
            404: 'Not Found - Genre not found',
            500: 'Internal Server Error - An error occurred while retrieving the genre',
        },
        description='Retrieve a genre by its unique identifier.',
        security='API Key',
        tags=['Genres'],
    )
    def get(self, genre_id):
        """
        Retrieve a genre by its unique identifier.

        This endpoint allows the retrieval of a genre by its unique identifier.

        Parameters:
            - genre_id (string, required): The unique identifier of the genre.

        Responses:
            200:
                description: OK - Genre retrieved successfully.
            404:
                description: Not Found - Genre not found.
            500:
                description: Internal Server Error - An error occurred while retrieving the genre.

        Security:
            - API Key

        Tags:
            - Genres

        Returns:
            A dictionary with the following keys:
            - 'msg' (str): A message indicating the success or failure of the request.
            - 'data' (dict): The retrieved genre object.
            - 'status' (int): A custom status code (e.g., 200 for OK, 404 for not found, 500 for an error).
        """
        try:
            genre = GenreService.get_genre_by_id(genre_id)
            if not genre:
                return {
                    'msg': 'Not Found - Genre not found', 
                    'data': None, 
                    'status': 404
                }, 404 # Not found
            return {
                'msg': 'OK - Genre retrieved successfully', 
                'data': genre.to_dict(), 
                'status': 200
            }, 200 # OK code status 200
        except Exception as e:
            if hasattr(e, 'code') and e.code is not None:
                return {
                    'msg': str(e), 
                    'data': None, 
                    'status': e.code
                }, e.code
            else:
                return {
                    'msg': 'Internal Server Error - An error occurred while retrieving the genre: ' + str(e),
                    'data': None,
                    'status': 500
                }, 500
    # PUT request to update a genre using public id
    @genre_namespace.expect(genre_model)
    @genre_namespace.doc(
        params={
            'genre_id': 'The unique identifier of the genre.'
        },
        responses={
            200: 'OK - Genre updated successfully',
            404: 'Not Found - Genre not found',
            500: 'Internal Server Error - An error occurred while updating the genre',
        },
        description='Update a genre by its unique identifier.',
        security='API Key',
        tags=['Genres'],
    )
    def put(self, genre_id):
        """
        Update a genre by its unique identifier.

        This endpoint allows the update of a genre by providing JSON data containing updated genre information.

        Parameters:
            - genre_id (string, required): The unique identifier of the genre.

        Responses:
            200:
                description: OK - Genre updated successfully.
            404:
                description: Not Found - Genre not found.
            500:
                description: Internal Server Error - An error occurred while updating the genre.

        Security:
            - API Key

        Tags:
            - Genres

        Returns:
            A dictionary with the following keys:
            - 'msg' (str): A message indicating the success or failure of the request.
            - 'data' (dict): The updated genre object.
            - 'status' (int): A custom status code (e.g., 200 for OK, 404 for not found, 500 for an error).
        """
        try:
            # Extract the request data (genre information) from the payload
            data = genre_namespace.payload

            # Check if a genre with the given ID exists
            existing_genre = GenreService.get_genre_by_id(genre_id)
            if not existing_genre:
                return {
                    'msg': 'Not Found - Genre not found',
                    'data': None,
                    'status': 404
                }, 404  # Not found

            # Update the genre using the GenreService
            updated_genre = GenreService.update_genre(genre_id, data)
            return {
                'msg': 'OK - Genre updated successfully',
                'data': updated_genre.to_dict(),
                'status': 200
            }, 200  # OK code status 200
        except Exception as e:
            if hasattr(e, 'code') and e.code is not None:
                return {
                    'msg': str(e),
                    'data': None,
                    'status': e.code
                }, e.code
            else:
                return {
                    'msg': 'Internal Server Error - An error occurred while updating the genre: ' + str(e),
                    'data': None,
                    'status': 500
                }, 500
    # PATCH request to parial update a genre using public id
    @genre_namespace.expect(genre_model)
    @genre_namespace.doc(
        params={
            'genre_id': 'The unique identifier of the genre.'
        },
        responses={
            200: 'OK - Genre partially updated successfully',
            404: 'Not Found - Genre not found',
            500: 'Internal Server Error - An error occurred while partially updating the genre',
        },
        description='Partially update a genre by its unique identifier.',
        security='API Key',
        tags=['Genres'],
    )
    def patch(self, genre_id):
        """
        Partially update a genre by its unique identifier.

        This endpoint allows the partial update of a genre by providing JSON data containing partially updated genre information.

        Parameters:
            - genre_id (string, required): The unique identifier of the genre.

        Responses:
            200:
                description: OK - Genre partially updated successfully.
            404:
                description: Not Found - Genre not found.
            500:
                description: Internal Server Error - An error occurred while partially updating the genre.

        Security:
            - API Key

        Tags:
            - Genres

        Returns:
            A dictionary with the following keys:
            - 'msg' (str): A message indicating the success or failure of the request.
            - 'data' (dict): The partially updated genre object.
            - 'status' (int): A custom status code (e.g., 200 for OK, 404 for not found, 500 for an error).
        """
        try:
            # Extract the request data (genre information) from the payload
            data = genre_namespace.payload

            # Check if a genre with the given ID exists
            existing_genre = GenreService.get_genre_by_id(genre_id)
            if not existing_genre:
                return {
                    'msg': 'Not Found - Genre not found',
                    'data': None,
                    'status': 404
                }, 404  # Not found

            # Partially update the genre using the GenreService
            partially_updated_genre = GenreService.update_partial_genre(genre_id, data)
            return {
                'msg': 'OK - Genre partially updated successfully',
                'data': partially_updated_genre,
                'status': 200
            }, 200  # OK code status 200
        except Exception as e:
            if hasattr(e, 'code') and e.code is not None:
                return {
                    'msg': str(e),
                    'data': None,
                    'status': e.code
                }, e.code
            else:
                return {
                    'msg': 'Internal Server Error - An error occurred while partially updating the genre: ' + str(e),
                    'data': None,
                    'status': 500
                }, 500
    # DELETE request to delete a genre using public id
    @genre_namespace.doc(
        params={
            'genre_id': 'The unique identifier of the genre.'
        },
        responses={
            204: 'No Content - Genre deleted successfully',
            404: 'Not Found - Genre not found',
            500: 'Internal Server Error - An error occurred while deleting the genre',
        },
        description='Delete a genre by its unique identifier.',
        security='API Key',
        tags=['Genres'],
    )
    def delete(self, genre_id):
        """
        Delete a genre by its unique identifier.

        This endpoint allows the deletion of a genre by its unique identifier.

        Parameters:
            - genre_id (string, required): The unique identifier of the genre.

        Responses:
            204:
                description: No Content - Genre deleted successfully.
            404:
                description: Not Found - Genre not found.
            500:
                description: Internal Server Error - An error occurred while deleting the genre.

        Security:
            - API Key

        Tags:
            - Genres

        Returns:
            A dictionary with the following keys:
            - 'msg' (str): A message indicating the success or failure of the request.
            - 'data' (dict): Additional information or data related to the request.
            - 'status' (int): A custom status code (e.g., 204 for no content, 404 for not found, 500 for an error).
        """
        try:
            # Check if a genre with the given ID exists
            existing_genre = GenreService.get_genre_by_id(genre_id)
            if not existing_genre:
                return {
                    'msg': 'Not Found - Genre not found',
                    'data': None,
                    'status': 404
                }, 404  # Not found

            # Delete the genre using the GenreService
            GenreService.delete_genre(genre_id)
            return {
                'msg': 'No Content - Genre deleted successfully',
                'data': None,
                'status': 204
            }, 204  # No content status code 204
        except Exception as e:
            if hasattr(e, 'code') and e.code is not None:
                return {
                    'msg': str(e),
                    'data': None,
                    'status': e.code
                }, e.code
            else:
                return {
                    'msg': 'Internal Server Error - An error occurred while deleting the genre: ' + str(e),
                    'data': None,
                    'status': 500
                }, 500
 
@genre_namespace.route('/exist/<string:genre_name>')
class GenreExistResource(Resource):
    @genre_namespace.doc(
        params={'genre_name': 'The genre name.'},
        responses={
            200: 'OK - Genre exists',
            404: 'Not Found - Genre does not exist',
        },
        description='Check if a genre exists by its unique identifier.',
        security='API Key',
        tags=['Genres'],
    )
    def get(self, genre_name):
        """
        Check if a genre exists by its unique identifier.

        This endpoint allows checking if a genre exists by its unique identifier.

        Parameters:
            - genre_id (string, required): The unique identifier of the genre.

        Responses:
            200:
                description: OK - Genre exists.
            404:
                description: Not Found - Genre does not exist.

        Security:
            - API Key

        Tags:
            - Genres

        Returns:
            A dictionary with a message indicating if the genre exists.
        """
        try:
            # Use GenreService to check if the genre exists
            if not GenreService.genre_exists(genre_name):
                return {
                    'msg': 'Not Found - Genre does not exist',
                    'data': None,
                    'status': 404
                }, 404
            return {
                'msg': 'OK - Genre exists',
                'data': None,
                'status': 200
            }, 200
        except Exception as e:
            if hasattr(e, 'code') and e.code is not None:
                return {
                    'msg': str(e),
                    'data': None,
                    'status': e.code
                }, e.code
            else:
                return {
                    'msg': 'Internal Server Error - An error occurred while retrieving genre count: ' + str(e),
                    'data': None,
                    'status': 500
                }, 500

@genre_namespace.route('/search/<string:query>')
class GenreSearchResource(Resource):
    @genre_namespace.doc(
        params={
            'query': 'Search query for genres.',
        },
        responses={
            200: 'OK - Genres retrieved successfully',
            404: 'Not Found - No genres found',
        },
        description='Search for genres using a query string.',
        security='API Key',
        tags=['Genres'],
    )
    def get(self, query):
        """
        Search for genres using a query string.

        This endpoint allows searching for genres based on a query string.

        Parameters:
            - query (string, required): Search query for genres.

        Responses:
            200:
                description: Genres retrieved successfully.
            404:
                description: Not Found - No genres found.

        Security:
            - API Key

        Tags:
            - Genres

        Returns:
            A list of genres matching the search query.
        """
        try:
            # query = genre_namespace.get('query')
            if query:
                # Use GenreService to search for genres based on the query
                matching_genres = GenreService.search_genres(query)
                if matching_genres:
                    return {
                        'msg': 'OK - Genres retrieved successfully', 
                        'data': matching_genres,
                        'status': 200
                    }, 200
                else:
                    return {
                        'msg': 'Not Found - No genres found', 
                        'data': [],
                        'status': 404
                    }, 404
            else:
                return {
                    'msg': 'Bad Request - Missing search query',
                    'data': None,
                    'status': 400
                }, 400
        except Exception as e:
            if hasattr(e, 'code') and e.code is not None:
                return {
                    'msg': str(e),
                    'data': None,
                    'status': e.code
                }, e.code
            else:
                return {
                    'msg': 'Internal Server Error - An error occurred while retrieving genre count: ' + str(e),
                    'data': None,
                    'status': 500
                }, 500
            
@genre_namespace.route('/count')
class GenreCountResource(Resource):
    @genre_namespace.doc(
        responses={
            200: 'OK - Genre count retrieved successfully',
            500: 'Internal Server Error - An error occurred while retrieving genre count',
        },
        description='Retrieve the count of all genres.',
        security='API Key',
        tags=['Genres'],
    )
    def get(self):
        """
        Retrieve the count of all genres.

        This endpoint allows retrieving the count of all genres.

        Responses:
            200:
                description: OK - Genre count retrieved successfully.
            500:
                description: Internal Server Error - An error occurred while retrieving genre count.

        Security:
            - API Key

        Tags:
            - Genres

        Returns:
            The count of all genres.
        """
        try:
            # Use GenreService to retrieve the count of all genres
            genre_count = GenreService.count_genres()
            return {
                'msg': 'OK - Genre count retrieved successfully', 
                'data': genre_count,
                'status': 200
            }, 200
        except Exception as e:
            if hasattr(e, 'code') and e.code is not None:
                return {
                    'msg': str(e),
                    'data': None,
                    'status': e.code
                }, e.code
            else:
                return {
                    'msg': 'Internal Server Error - An error occurred while retrieving genre count: ' + str(e),
                    'data': None,
                    'status': 500
                }, 500
            
@genre_namespace.route('/archive/<string:genre_id>')
class GenreArchiveResource(Resource):
    # DELETE request to delete a genre using public id
    @genre_namespace.doc(
        params={
            'genre_id': 'The unique identifier of the genre.'
        },
        responses={
            204: 'No Content - Genre deleted successfully',
            404: 'Not Found - Genre not found',
            500: 'Internal Server Error - An error occurred while deleting the genre',
        },
        description='Delete a genre by its unique identifier.',
        security='API Key',
        tags=['Genres'],
    )
    def delete(self, genre_id):
        """
        Delete a genre by its unique identifier.

        This endpoint allows the deletion of a genre by its unique identifier.

        Parameters:
            - genre_id (string, required): The unique identifier of the genre.

        Responses:
            204:
                description: No Content - Genre deleted successfully.
            404:
                description: Not Found - Genre not found.
            500:
                description: Internal Server Error - An error occurred while deleting the genre.

        Security:
            - API Key

        Tags:
            - Genres

        Returns:
            A dictionary with the following keys:
            - 'msg' (str): A message indicating the success or failure of the request.
            - 'data' (dict): Additional information or data related to the request.
            - 'status' (int): A custom status code (e.g., 204 for no content, 404 for not found, 500 for an error).
        """
        try:
            # Check if a genre with the given ID exists
            existing_genre = GenreService.get_genre_by_id(genre_id)
            print("Genre: ",existing_genre)
            if not existing_genre:
                return {
                    'msg': 'Not Found - Genre not found',
                    'data': None,
                    'status': 404
                }, 404  # Not found

            # Delete the genre using the GenreService
            GenreService.archive_genre(genre_id)
            return {
                'msg': 'No Content - Genre deleted successfully',
                'data': None,
                'status': 204
            }, 204  # No content status code 204
        except Exception as e:
            if hasattr(e, 'code') and e.code is not None:
                return {
                    'msg': str(e),
                    'data': None,
                    'status': e.code
                }, e.code
            else:
                return {
                    'msg': 'Internal Server Error - An error occurred while deleting the genre: ' + str(e),
                    'data': None,
                    'status': 500
                }, 500
 