from flask import request
from flask_restx import Resource, Namespace, fields
from app.services.channel_service import ChannelService
from app.schema.channel import channel_dict, ChannelSchema

# Create a Flask-RESTx namespace named 'channels' with a description
channel_namespace = Namespace('channels', path='/channels', description='Channel-related operations')
# Add the channel model to the namespace
channel_model = channel_namespace.model('Channel', channel_dict)

@channel_namespace.route('/')
class ChannelListResource(Resource):
    """
    Resource for managing a list of channels.

    Attributes:
        None

    Methods:
        - get: Retrieve a list of all channels.
        - post: Create a new channel.
        - delete: Delete all channels.

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

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

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

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

        Security:
            - API Key

        Tags:
            - Channels

        Returns:
            A dictionary with the following keys:
            - 'msg' (str): A message indicating the success or failure of the request.
            - 'data' (list): A list of channel objects representing various channels.
            - 'status' (int): A custom status code (e.g., 200 for success, 404 for not found, 500 for an error).
        """
        try:
            # Attempt to fetch all channels using the ChannelService
            channels = ChannelService.get_all_channels()
            if not channels:
                # No channels found, return a not found response
                return {
                    'msg': 'No channels found',
                    'data': [],
                    'status': 204
                }, 204  # Not found status code
            # Channels found, return success response
            # Return a dictionary with message, data, and custom status code
            return {
                'msg': 'Channels retrieved successfully',
                'data': channels,
                '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
            # If no specific status code is provided, return a 500 Internal Server Error
            return {
                'msg': 'An error occurred while retrieving channels: ' + str(e),
                'data': None,
                'status': 500
            }, 500  # Internal Server Error

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

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

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

        Security:
            - API Key

        Tags:
            - Channels

        Parameters:
            - channel_data (JSON, required): JSON data containing channel information (channel_model).

        Request Body:
            - channel_model (object): The data format for creating a new channel.

        Returns:
            A dictionary with the following keys:
            - 'msg' (str): A message indicating the success or failure of the request.
            - 'data' (dict): The newly created channel 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 (channel information) from the payload
            data = channel_namespace.payload

            # Validate the incoming data using ChannelSchema
            channel_schema = ChannelSchema()
            errors = channel_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 channel with the given name already exists
            existing_channel = ChannelService.get_channels_by_name(data['name'])
            if existing_channel:
                return {
                    'msg': 'Conflict - Channel with this name already exists',
                    'data': None,
                    'status': 409
                }, 409  # Conflict

            # Create a new channel using the ChannelService
            new_channel = ChannelService.create_channel(data)
            # Return the newly created channel as the API response with HTTP status 201 (Created)
            return {
                'msg': 'New channel created successfully',
                'data': new_channel,
                '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
            # If no specific status code is provided, return a 500 Internal Server Error
            return {
                'msg': 'An error occurred while creating a new channel: ' + str(e),
                'data': None,
                'status': 500
            }, 500  # Internal Server Error

    # DELETE request to delete all channels
    @channel_namespace.doc(
        responses={
            204: 'No Content - All channels deleted successfully',
            404: 'Not Found - No channels found',
            500: 'Internal Server Error - An error occurred while deleting channels',
        },
        description='Delete all channels.',
        security='API Key',
        tags=['Channels'],
    )
    def delete(self):
        """
        Delete all channels.

        This endpoint allows the deletion of all channels.

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

        Security:
            - API Key

        Tags:
            - Channels

        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 channels using the ChannelService and get the count of deleted channels
            deleted_count = ChannelService.delete_all_channels()
            if deleted_count > 0:
                # Return a success message with HTTP status 204 (No Content)
                return {
                    'msg': f'{deleted_count} channels deleted',
                    'data': None,
                    'status': 204
                }, 204  # No content
            else:
                # Return a message indicating that no channels were found with HTTP status 404 (Not Found)
                return {
                    'msg': 'No channels 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 
            # If no specific status code is provided, return a 500 Internal Server Error
            return {
                'msg': 'Internal Server Error - An error occurred while deleting channels: ' + str(e),
                'data': None,
                'status': 500
            }, 500  # Internal Server Error

@channel_namespace.route('/<string:channel_id>')
class ChannelResource(Resource):
    @channel_namespace.doc(
        params={
            'channel_id': 'The unique identifier of the channel.'
        },
        responses={
            200: 'OK - Channel retrieved successfully',
            404: 'Not Found - Channel not found',
            500: 'Internal Server Error - An error occurred while retrieving the channel',
        },
        description='Retrieve a channel by its unique identifier.',
        security='API Key',
        tags=['Channels'],
    )
    def get(self, channel_id):
        try:
            channel = ChannelService.get_channel_by_id(channel_id)
            if not channel:
                return {
                    'msg': 'Not Found - Channel not found',
                    'data': None,
                    'status': 404
                }, 404
            return {
                'msg': 'OK - Channel retrieved successfully',
                'data': channel.to_dict(),
                '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 the channel: ' + str(e),
                    'data': None,
                    'status': 500
                }, 500

    @channel_namespace.expect(channel_model)
    @channel_namespace.doc(
        params={
            'channel_id': 'The unique identifier of the channel.'
        },
        responses={
            200: 'OK - Channel updated successfully',
            404: 'Not Found - Channel not found',
            500: 'Internal Server Error - An error occurred while updating the channel',
        },
        description='Update a channel by its unique identifier.',
        security='API Key',
        tags=['Channels'],
    )
    def put(self, channel_id):
        try:
            data = channel_namespace.payload
            existing_channel = ChannelService.get_channel_by_id(channel_id)
            if not existing_channel:
                return {
                    'msg': 'Not Found - Channel not found',
                    'data': None,
                    'status': 404
                }, 404

            updated_channel = ChannelService.update_channel(channel_id, data)
            return {
                'msg': 'OK - Channel updated successfully',
                'data': updated_channel.to_dict(),
                '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 updating the channel: ' + str(e),
                    'data': None,
                    'status': 500
                }, 500

    @channel_namespace.expect(channel_model)
    @channel_namespace.doc(
        params={
            'channel_id': 'The unique identifier of the channel.'
        },
        responses={
            200: 'OK - Channel partially updated successfully',
            404: 'Not Found - Channel not found',
            500: 'Internal Server Error - An error occurred while partially updating the channel',
        },
        description='Partially update a channel by its unique identifier.',
        security='API Key',
        tags=['Channels'],
    )
    def patch(self, channel_id):
        try:
            data = channel_namespace.payload
            existing_channel = ChannelService.get_channel_by_id(channel_id)
            if not existing_channel:
                return {
                    'msg': 'Not Found - Channel not found',
                    'data': None,
                    'status': 404
                }, 404

            partially_updated_channel = ChannelService.update_partial_channel(channel_id, data)
            return {
                'msg': 'OK - Channel partially updated successfully',
                'data': partially_updated_channel,
                '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 partially updating the channel: ' + str(e),
                    'data': None,
                    'status': 500
                }, 500

    @channel_namespace.doc(
        params={
            'channel_id': 'The unique identifier of the channel.'
        },
        responses={
            204: 'No Content - Channel deleted successfully',
            404: 'Not Found - Channel not found',
            500: 'Internal Server Error - An error occurred while deleting the channel',
        },
        description='Delete a channel by its unique identifier.',
        security='API Key',
        tags=['Channels'],
    )
    def delete(self, channel_id):
        try:
            existing_channel = ChannelService.get_channel_by_id(channel_id)
            if not existing_channel:
                return {
                    'msg': 'Not Found - Channel not found',
                    'data': None,
                    'status': 404
                }, 404

            ChannelService.delete_channel(channel_id)
            return {
                'msg': 'No Content - Channel deleted successfully',
                'data': None,
                'status': 204
            }, 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 channel: ' + str(e),
                    'data': None,
                    'status': 500
                }, 500

@channel_namespace.route('/exist/<string:channel_name>')
class ChannelExistResource(Resource):
    @channel_namespace.doc(
        params={'channel_name': 'The channel name.'},
        responses={
            200: 'OK - Channel exists',
            404: 'Not Found - Channel does not exist',
        },
        description='Check if a channel exists by its unique identifier.',
        security='API Key',
        tags=['Channels'],
    )
    def get(self, channel_name):
        try:
            if not ChannelService.channel_exists(channel_name):
                return {
                    'msg': 'Not Found - Channel does not exist',
                    'data': None,
                    'status': 404
                }, 404
            return {
                'msg': 'OK - Channel 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 channel count: ' + str(e),
                    'data': None,
                    'status': 500
                }, 500

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

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

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

        Responses:
            200:
                description: OK - Channels retrieved successfully.
            404:
                description: Not Found - No channels found.

        Security:
            - API Key

        Tags:
            - Channels

        Returns:
            A list of channels matching the search query.
        """
        try:
            if query:
                matching_channels = ChannelService.search_channels(query)
                if matching_channels:
                    return {
                        'msg': 'OK - Channels retrieved successfully', 
                        'data': matching_channels,
                        'status': 200
                    }, 200
                else:
                    return {
                        'msg': 'Not Found - No channels 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 
            return {
                'msg': 'Internal Server Error - An error occurred while retrieving channel count: ' + str(e),
                'data': None,
                'status': 500
            }, 500


