import logging
import os
import random
import requests
import textwrap
from urllib.parse import quote, urljoin, urlencode

import bpy

log = logging.getLogger("comfyui_blender")


def add_custom_headers(headers=None):
    """Compose the URL for a ComfyUI WebSocket server route."""

    if headers is None:
        headers = {}
    addon_prefs = bpy.context.preferences.addons["comfyui_blender"].preferences
    for header in addon_prefs.http_headers:
        if header.key:
            headers[header.key] = header.value
    return headers


def contains_non_latin(s):
    """Check if the string contains any non-Latin characters."""

    latin_ranges = (
        (0x0041, 0x005A),  # A-Z
        (0x0061, 0x007A),  # a-z
        (0x00C0, 0x00D6),  # À-Ö (Latin-1 Supplement)
        (0x00D8, 0x00F6),  # Ø-ö
        (0x00F8, 0x00FF),  # ø-ÿ
    )
    return any(not any(start <= ord(char) <= end for start, end in latin_ranges) and ord(char) > 127 for char in s)


def download_file(filename, subfolder, type="output"):
    """Download a file from the ComfyUI server."""

    addon_prefs = bpy.context.preferences.addons["comfyui_blender"].preferences
    outputs_folder = addon_prefs.outputs_folder

    # Download the file data from the ComfyUI server
    # Add a random parameter to avoid caching issues
    params = {"filename": filename, "subfolder": subfolder, "type": type, "rand": random.random()}
    url = get_server_url("/view", params=params)

    headers = {"Content-Type": "application/json"}
    headers = add_custom_headers(headers)
    try:
        # Download with streaming to handle large files and avoid memory issues
        response = requests.get(url, params=params, headers=headers, stream=True)
    except Exception as e:
        error_message = f"Failed to download file from ComfyUI server: {url}. {e}"
        log.exception(error_message)
        raise error_message
        # bpy.ops.comfy.show_error_popup("INVOKE_DEFAULT", error_message=error_message)
        # This triggers RuntimeError: Operator bpy.ops.comfy.show_error_popup.poll() Missing 'window' in context
        # To be fixed in future release
    
    if response.status_code != 200:
        error_message = error_message = f"Failed to download file from ComfyUI server: {url}."
        log.error(error_message)
        raise error_message
        # bpy.ops.comfy.show_error_popup("INVOKE_DEFAULT", error_message=error_message)
        # This triggers RuntimeError: Operator bpy.ops.comfy.show_error_popup.poll() Missing 'window' in context
        # To be fixed in future release

    # Save the file in the output folder
    folder = os.path.join(outputs_folder, subfolder)
    filepath = os.path.join(folder, filename)

    # Create subfolder if it does not exist
    os.makedirs(folder, exist_ok=True)

    with open(filepath, "wb") as file:
        for chunk in response.iter_content(chunk_size=8192):
            if chunk:  # Filter out keep-alive chunks
                file.write(chunk)


def get_filepath(filename, folder):
    """Handle file names conflicts when importing files, by appending an incremental number"""

    filepath = os.path.join(folder, filename)    
    if os.path.exists(filepath):
        name, ext = os.path.splitext(filename)
        counter = 1
        while os.path.exists(os.path.join(folder, f"{name}_{counter}{ext}")):
            counter += 1
        
        # Rename input file
        filename = f"{name}_{counter}{ext}"
        filepath = os.path.join(folder, filename)
    return filename, filepath


def get_server_url(route=None, params=None):
    """Compose the URL for a ComfyUI server route."""

    addon_prefs = bpy.context.preferences.addons["comfyui_blender"].preferences
    server_address = addon_prefs.server_address
    if route:
        server_url = urljoin(server_address, quote(route))
    if params:
        server_url = f"{server_url}?{urlencode(params)}"
    return server_url


def get_websocket_url(route=None, params=None):
    """Compose the URL for a ComfyUI WebSocket server route."""

    url = get_server_url(route=route, params=params)
    # Replace http with ws and https with wss
    if "https://" in url:
        url = url.replace("https://", "wss://")
    elif "http://" in url:
        url = url.replace("http://", "ws://")
    return url


# This method has been replaced by the operator show_error_message
# The operator provides a OK button to ensure the popup does not disappear immediately
def show_error_popup(message):
    """Show an error popup."""

    def draw(self, context):
        self.layout

        # Wrap text to specified width
        wrapped_lines = textwrap.wrap(message, width=70)
        for line in wrapped_lines:
            self.layout.label(text=line)

    bpy.context.window_manager.popup_menu(draw, title="Execution Error", icon="ERROR")


def upload_file(filepath, type, subfolder=None, overwrite=False):
    """Upload a file to the ComfyUI server."""

    # Prepare form data
    data = {}
    if overwrite:
        data["overwrite"] = True

    # Read file data
    with open(filepath, "rb") as file:
        file_data = file.read()

    # Extract filename from the filepath
    filename = os.path.basename(filepath)

    # Build request according to the file type
    if type == "3d":
        data["subfolder"] = "3d"
        if subfolder:
            data["subfolder"] = os.path.join(data["subfolder"], subfolder)

    elif type == "image":
        if subfolder:
            data["subfolder"] = subfolder

    files = {"image": (filename, file_data)}
    url = get_server_url("/upload/image")
    headers = add_custom_headers()
    response = requests.post(url, files=files, data=data, headers=headers)
    return response
