import httpx
import json
import logging
import re
from typing import Any
from fastapi.responses import HTMLResponse
from starlette.concurrency import run_in_threadpool
from jinja2 import Environment, Template
from app.api.v1.libraries.chatgptasync import get_response_gpt
from app.api.v1.libraries.chromadbRAG import truncate_content

class ResponseHandler:
    def handle_response(self, response: Any, response_schema: dict = None, template_content: str=None):
        """
        Process the API response based on the provided response schema.
        
        :param response: Response object or data.
        :param response_schema: Schema of the expected API response.
        :return: Processed response data.
        """
        if isinstance(response, httpx.Response):
            status_code = response.status_code
            if status_code in [200, 201]:
                return self._handle_success(response, response_schema, template_content)
            else:
                return self._handle_error(response)
        else:
            response = self._handle_non_httpx_response(response, response_schema, template_content)
            return response

    async def _handle_success(self, response: httpx.Response, response_schema: dict, template_content: Template):
        """
        Handle a successful API response by formatting it into HTML, text, or JSON.
        
        :param response: Successful response object.
        :param response_schema: Schema of the expected API response.
        :return: Appropriately formatted response data.
        """
        try:
            if "application/json" in response_schema.get("200", {}).get("content", {}):
                response_type = "application/json"
            else:
                response_type = None

            # Process JSON responses
            if response_type == "application/json":
                try:
                    json_data = response.json()
                    #json_data_str = json.dumps(json_data, indent=2)
                except json.JSONDecodeError:
                    print("No JSON body found.")
                    json_data = {}

                print("JSON Data", json_data)
                if template_content:
                    # Apply the template to the JSON data
                    environment = Environment()
                    template = environment.from_string(template_content)
                    html_content = template.render(json_data=json_data)
                    #print(html_content)
                    return html_content
                else:
                    # Fallback to dynamic HTML generation or showing a default message
                    description = response_schema.get("200", {}).get("description", "No description provided.")
                    return f"<p>{description}</p>"

            # Process non-JSON responses directly
            else:
                description = response_schema.get("200", {}).get("description", "No description provided.")
                # Directly return the description for non-JSON types or when no specific handling is defined
                return f"<p>{description}</p>"

        except Exception as e:
            logging.error(f"Failed to process the successful response: {e}")
            return "<p>Error processing the response.</p>"

    async def _handle_error(self, response: httpx.Response):
        """
        Handle an error in the API response.
        
        :param response: Error response object.
        :return: Parsed error information.
        """
        logging.error(f"API Request failed: {response.status_code} - {response.text}")
        return {"error": response.status_code, "message": response.text}

    async def _handle_non_httpx_response(self, response: Any, response_schema: dict, template_content: str):
        """
        Handle non-HTTPX responses by rendering them with a given template.
        
        :param response: The direct response data, which can be a string or a dictionary.
        :param response_schema: Schema of the expected API response. Not used in this revision, but kept for future use.
        :param template_content: The template content as a string to be used for rendering the response.
        :return: Rendered HTML as a normal text string.
        """
        try:
            # Check if response is a string and try to convert it to a dictionary
            if isinstance(response, str):
                try:
                    response_data = json.loads(response)
                except json.JSONDecodeError as json_err:
                    logging.error(f"Error decoding JSON string: {json_err}")
                    return "<h1>Invalid JSON format</h1>"
            else:
                response_data = response

            # Debug print of the response data
            #print("Processed response data: ", json.dumps(response_data, indent=2))
            # Ensure template_content is not None or empty before attempting to render
            if template_content:
                # Assuming template_content is a string that contains the template
                #print(" Template? ", template_content)
                template_env = Environment()
                template = template_env.from_string(template_content)
                html_content = template.render(json_data=response_data)  # Render the template with response data
                #print(html_content)
                return html_content  # Return HTML content directly as a normal text string
            else:
                description = response_schema.get("200", {}).get("description", "No description provided.")
                return f"<p>{description}</p>"

        except Exception as e:
            logging.error(f"Failed to process response: {e}")
            return f"<h1>Error rendering response</h1><p>{e}</p>"

    async def dynamichtml(json_data_str):
        
        json_data_str = truncate_content(json_data_str, 2000)

        # Construct the prompt for GPT, adapting the provided sample code
        user_message = f"Convert the following JSON data into HTML format:\n{json_data_str}"

        composed_message = (
            f"Please format the provided JSON data into HTML to make it more user friendly to read according to type of content. Please don't introduce describe or give any status. Just convert the JSON to a html and fit into screen. Don't use tables."
        )

        system_message = {"role": "system", "content": composed_message}
        content_user = f"User: {user_message}"

        user_message = {"role": "user", "content": content_user}
        
        # Here you call GPT with the composed system and user messages
        # This assumes get_response_gpt is adapted to handle these structured messages
        llm_response = await run_in_threadpool(get_response_gpt, system_message, user_message, 'gpt-3.5-turbo')
        html_response = llm_response.get("bot_response")
        cleaned_html_content = html_response.strip("```html").strip("```").strip()
        pattern = re.compile(r'^(.*(?:Status: success|JSON To HTML).*)$', re.IGNORECASE | re.MULTILINE)

        # Replace matched lines with an empty string
        cleaned_html_content = re.sub(pattern, '', cleaned_html_content)
        # Optionally, remove any extra newlines that may be left after the removal
        cleaned_html_content = re.sub(r'\n\s*\n', '\n', cleaned_html_content)

        return cleaned_html_content