"""Resolve file URIs, URLs, data URIs, and base64 strings to local file paths."""

import base64
import binascii
import mimetypes
import os
import random
import tempfile
import urllib.parse
from functools import lru_cache

import requests

from litemind.utils.temp_file_manager import register_temp_dir
from litemind.utils.uri_utils import is_uri, is_valid_path


@lru_cache
def uri_to_local_file_path(file_uri: str) -> str:
    """
    Resolve a file URI to a local file path.

    Handles multiple URI schemes:

    1. Local file paths -- returns the absolute path directly.
    2. ``file://`` URIs -- converts to a local path.
    3. Remote URLs (``http``, ``https``, ``ftp``) -- downloads to a temp file.
    4. ``data:`` URIs or raw Base64 strings -- decodes to a temp file.

    Results are cached via ``lru_cache`` to avoid redundant downloads.

    Parameters
    ----------
    file_uri : str
        The file URI to resolve, or a local file path.

    Returns
    -------
    str
        The absolute path to the local file.

    Raises
    ------
    ValueError
        If the URI is empty, the local file is not found, the download
        fails, the Base64 data is invalid, or the URI scheme is unsupported.
    """
    if not file_uri:
        raise ValueError("Empty or null file URI provided")

    # SCENARIO 0: Check if it's already a valid local file path
    if is_valid_path(file_uri) and os.path.exists(file_uri):
        # If it's a valid local path, return its absolute path
        return os.path.abspath(file_uri)

    # Parse the URI to determine its type
    parsed = urllib.parse.urlparse(file_uri)

    # SCENARIO 1: file:// URI
    if parsed.scheme == "file":
        local_path = urllib.parse.unquote(parsed.path)
        # On Windows, remove leading slash if present
        if os.name == "nt" and local_path.startswith("/"):
            local_path = local_path[1:]
        local_path = os.path.abspath(local_path)
        if not os.path.exists(local_path):
            raise ValueError(f"Local file not found: '{local_path}'")
        return local_path

    # List of possible user agents
    user_agents = [
        "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/110.0.0.0 Safari/537.36",
        "Mozilla/5.0 (Windows NT 6.1; Win64; x64; rv:102.0) Gecko/20100101 Firefox/102.0",
        "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/15.3 Safari/605.1.15",
        "Mozilla/5.0 (Linux; Android 12; SM-G991B) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/104.0.0.0 Mobile Safari/537.36",
        "Mozilla/5.0 (iPhone; CPU iPhone OS 15_4 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/15.4 Mobile/15E148 Safari/604.1",
        "Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:98.0) Gecko/20100101 Firefox/98.0",
        "Mozilla/5.0 (iPad; CPU OS 15_0 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/15.0 Mobile/15E148 Safari/604.1",
        "Mozilla/5.0 (Windows NT 6.1; Trident/7.0; rv:11.0) like Gecko",
        "Mozilla/5.0 (Macintosh; Intel Mac OS X 12_4_0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/103.0.0.0 Safari/537.36",
        "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.114 Safari/537.36",
    ]

    # SCENARIO 2: Remote URL (http/https/ftp)
    if parsed.scheme in ["http", "https", "ftp"]:
        selected_user_agent = random.choice(user_agents)
        headers = {"User-Agent": selected_user_agent}

        try:
            with requests.get(file_uri, headers=headers, stream=True) as r:
                r.raise_for_status()

                # Derive filename from the URL path or Content-Type
                filename = os.path.basename(parsed.path)
                if not filename or "." not in filename:
                    content_type = r.headers.get("Content-Type", "")
                    guessed_ext = mimetypes.guess_extension(content_type.split(";")[0])
                    if guessed_ext is None:
                        guessed_ext = ".bin"
                    if not filename:
                        filename = f"download{guessed_ext}"
                    else:
                        filename += guessed_ext

                # Create a temp directory, store the file there
                temp_dir = register_temp_dir(tempfile.mkdtemp(prefix="download_"))
                local_path = os.path.join(temp_dir, filename)

                with open(local_path, "wb") as f:
                    for chunk in r.iter_content(chunk_size=8192):
                        f.write(chunk)

            return os.path.abspath(local_path)
        except requests.exceptions.RequestException as e:
            raise ValueError(
                f"Failed to download from URL: {file_uri}. Error: {str(e)}"
            )

    # SCENARIO 3: data: URI
    mime_type = None
    b64_data = file_uri

    if parsed.scheme == "data":
        if "," not in file_uri:
            raise ValueError(f"Invalid data URI format: {file_uri}")

        header, b64_data = file_uri.split(",", 1)
        # Extract MIME type from header
        if ";" in header:
            mime_type = header.split(":", 1)[1].split(";", 1)[0]

    # If it's not a recognized URI scheme, try to interpret as Base64
    elif not is_uri(file_uri):
        # Try to validate if it looks like base64
        try:
            # Check if padding is correct
            padding_needed = len(b64_data) % 4
            if padding_needed:
                b64_data += "=" * (4 - padding_needed)

            # Verify it can be decoded
            base64.b64decode(b64_data, validate=True)
        except (binascii.Error, ValueError) as e:
            raise ValueError(
                f"Invalid URI format and not valid Base64 data: '{file_uri}'. Error: {str(e)}"
            )
    else:
        raise ValueError(
            f"Unsupported URI scheme: '{parsed.scheme}'. Supported schemes: file, http, https, ftp, data"
        )

    # Decode base64 data
    try:
        file_data = base64.b64decode(b64_data)
    except (binascii.Error, ValueError) as e:
        raise ValueError(f"Failed to decode Base64 data: {str(e)}")

    # Decide on a filename with appropriate extension
    ext = None
    if mime_type:
        ext = mimetypes.guess_extension(mime_type)
    if not ext:
        ext = ".bin"

    temp_dir = register_temp_dir(tempfile.mkdtemp(prefix="b64_"))
    filename = f"decoded{ext}"
    local_path = os.path.join(temp_dir, filename)

    with open(local_path, "wb") as f:
        f.write(file_data)

    return os.path.abspath(local_path)
