# tools/clipboard.py - macOS clipboard and view_image tools.
from __future__ import annotations

import os
import re
import subprocess
import sys
import time

from langchain_core.tools import tool

from tools._logging import _debug_log, _emit_input_status
from tools._macos_bridge import bridge_request, get_bridge_config
from tools.live_view_visuals import emit_live_view_preview, image_to_preview_base64
from utils.path_safety import is_dangerous_path

try:
    import requests
except ImportError:
    requests = None


@tool
def get_clipboard_text() -> str:
    """Read the current text content from the macOS clipboard."""
    _emit_input_status("Reading clipboard…")
    if sys.platform != "darwin":
        return "Clipboard text is only supported on macOS."
    try:
        proc = subprocess.run(
            ["/usr/bin/pbpaste"],
            capture_output=True,
            timeout=5,
            check=False,
        )
    except Exception as e:
        return f"Failed to read clipboard text: {e}"
    if proc.returncode != 0:
        err = (proc.stderr or b"").decode("utf-8", errors="replace").strip()
        return f"Failed to read clipboard text: {err or 'pbpaste returned a non-zero exit code.'}"
    text = (proc.stdout or b"").decode("utf-8", errors="replace")
    if not text:
        return "Clipboard is empty or does not contain text."
    return text


@tool
def set_clipboard_text(text: str) -> str:
    """Write the given text to the macOS clipboard."""
    _emit_input_status("Copying to clipboard…")
    if not text:
        return "No text provided to copy."
    if sys.platform != "darwin":
        return "Clipboard text is only supported on macOS."
    try:
        proc = subprocess.run(
            ["/usr/bin/pbcopy"],
            input=text.encode("utf-8"),
            capture_output=True,
            timeout=5,
            check=False,
        )
    except Exception as e:
        return f"Failed to copy text to clipboard: {e}"
    if proc.returncode != 0:
        err = (proc.stderr or b"").decode("utf-8", errors="replace").strip()
        return f"Failed to copy text to clipboard: {err or 'pbcopy returned a non-zero exit code.'}"
    return "Text copied to clipboard successfully."


@tool
def get_clipboard_image(save_path: str = None) -> str:
    """If an image is on the clipboard, save it to disk and return the path.
    If no save_path provided, saves to a temp file in ~/.mugi/temp_clipboard.png
    Returns the saved path or an error message."""
    _emit_input_status("Grabbing clipboard image…")
    cfg, err = get_bridge_config()
    if not cfg:
        return f"Clipboard image bridge unavailable: {err}"
    status, image_data, req_err = bridge_request(cfg, "/clipboard/image", timeout=20)
    if req_err:
        return f"Failed to reach clipboard bridge: {req_err}"
    if status == 404:
        return "No image found on clipboard."
    if status != 200:
        return f"Clipboard bridge error (HTTP {status})."
    if not image_data:
        return "No image found on clipboard."
    if save_path is None or (isinstance(save_path, str) and not save_path.strip()):
        temp_dir = os.path.expanduser("~/.mugi")
        os.makedirs(temp_dir, exist_ok=True)
        save_path = os.path.join(temp_dir, f"temp_clipboard_{int(time.time())}.png")
    else:
        save_path = os.path.expanduser(save_path.strip())
        if is_dangerous_path(save_path):
            return f"Blocked: save path is in a protected location: {save_path}"
        from utils.user_model import enforce_low_level_user_model_write_guard

        _um_err = enforce_low_level_user_model_write_guard(
            os.path.abspath(save_path), mode="overwrite"
        )
        if _um_err:
            return _um_err
    try:
        with open(save_path, "wb") as f:
            f.write(image_data)
        try:
            with open(save_path, "rb") as f:
                b64 = image_to_preview_base64(f.read(), max_width=400)
            if b64:
                emit_live_view_preview(
                    "Clipboard image saved",
                    tool_name="get_clipboard_image",
                    preview_image_base64=b64,
                )
        except Exception:
            pass
        return f"Image saved to: {save_path}"
    except Exception as e:
        return f"Failed to save clipboard image: {e}"


@tool
def copy_image_to_clipboard(image_path: str) -> str:
    """Copy an image from a local file to the macOS clipboard so the user can paste (Cmd+V) elsewhere.
    Supports .png, .jpg, .gif and other formats NSImage can read."""
    _emit_input_status("Copying image to clipboard…")
    cfg, err = get_bridge_config()
    if not cfg:
        return f"Clipboard image bridge unavailable: {err}"
    path = (image_path or "").strip()
    if not path:
        _debug_log("[copy_image_to_clipboard] Empty path", "WARN")
        return "No image path provided."
    abs_path = os.path.abspath(os.path.expanduser(path))
    if is_dangerous_path(abs_path):
        _debug_log(f"[copy_image_to_clipboard] Blocked dangerous path: {abs_path}", "WARN")
        return "Blocked: path is in a protected location."
    if not os.path.isfile(abs_path):
        _debug_log(f"[copy_image_to_clipboard] Not a file or missing: {abs_path}", "WARN")
        return f"Not a file or not found: {abs_path}"
    try:
        with open(abs_path, "rb") as f:
            data = f.read()
        ext = os.path.splitext(abs_path)[1].lower()
        ctype = "image/jpeg" if ext in {".jpg", ".jpeg"} else "image/png"
        status, _, req_err = bridge_request(
            cfg,
            "/clipboard/image",
            method="POST",
            headers={"Content-Type": ctype},
            body=data,
            timeout=20,
        )
        if req_err:
            return f"Failed to reach clipboard bridge: {req_err}"
        if status != 200:
            return f"Clipboard bridge error (HTTP {status})."
        try:
            b64 = image_to_preview_base64(data, max_width=400)
            if b64:
                emit_live_view_preview(
                    "Image copied to clipboard",
                    tool_name="copy_image_to_clipboard",
                    preview_image_base64=b64,
                )
        except Exception:
            pass
        return "Image copied — paste with Cmd+V"
    except Exception as e:
        _debug_log(f"[copy_image_to_clipboard] Error: {e}", "ERROR")
        return f"Failed to copy image to clipboard: {e}"


@tool
def copy_image_from_url_to_clipboard(url: str) -> str:
    """Download an image from a URL and copy it to the macOS clipboard so the user can paste (Cmd+V) elsewhere."""
    _emit_input_status("Downloading image…")
    cfg, err = get_bridge_config()
    if not cfg:
        return f"Clipboard image bridge unavailable: {err}"
    if requests is None:
        return "requests library is required; install with: pip install requests"
    url = re.sub(r"\s+", "", url or "")
    if not url or not url.startswith(("http://", "https://")):
        return "Invalid or missing URL (must start with http:// or https://)."
    try:
        resp = requests.get(url, timeout=15, stream=True)
        resp.raise_for_status()
        data = resp.content
        if not data:
            return "URL returned no data."
        content_type = (resp.headers.get("Content-Type") or "").lower()
        if "jpeg" in content_type or "jpg" in content_type:
            ctype = "image/jpeg"
        else:
            ctype = "image/png"
        status, _, req_err = bridge_request(
            cfg,
            "/clipboard/image",
            method="POST",
            headers={"Content-Type": ctype},
            body=data,
            timeout=20,
        )
        if req_err:
            return f"Failed to reach clipboard bridge: {req_err}"
        if status != 200:
            return f"Clipboard bridge error (HTTP {status})."
        try:
            b64 = image_to_preview_base64(data, max_width=400)
            if b64:
                emit_live_view_preview(
                    "Image from URL copied to clipboard",
                    tool_name="copy_image_from_url_to_clipboard",
                    preview_image_base64=b64,
                    url=url,
                )
        except Exception:
            pass
        return "Image copied — paste with Cmd+V"
    except requests.exceptions.Timeout:
        return "Request timed out. User can paste the image from their browser if they have it open."
    except requests.exceptions.RequestException as e:
        return f"Download failed: {e}. User can paste the image from their browser if they have it open."
    except Exception as e:
        _debug_log(f"[copy_image_from_url_to_clipboard] Error: {e}", "ERROR")
        return f"Failed to copy image to clipboard: {e}. User can paste the image from their browser if they have it open."


@tool
def copy_current_clipboard_image_again() -> str:
    """Re-copy the image currently on the clipboard (e.g. after another app may have altered it). Paste with Cmd+V."""
    _emit_input_status("Re-copying image…")
    cfg, err = get_bridge_config()
    if not cfg:
        return f"Clipboard image bridge unavailable: {err}"
    status, _, req_err = bridge_request(cfg, "/clipboard/image/refresh", method="POST", timeout=15)
    if req_err:
        return f"Failed to reach clipboard bridge: {req_err}"
    if status == 404:
        return "No image on clipboard."
    if status != 200:
        return f"Clipboard bridge error (HTTP {status})."
    return "Clipboard image re-copied — paste with Cmd+V"


@tool
def view_image(image_url: str) -> str:
    """Fallback when you need to reference an image by URL. Returns a short note that the image is available at the given URL.
    Use this only if the image was not already included in the user's message. In normal vision flows the image is already in the conversation."""
    _emit_input_status("Viewing image…")
    _debug_log(f"[view_image] URL: {image_url[:80] if image_url else ''}...", "DEBUG")
    url = re.sub(r"\s+", "", image_url or "")
    if not url:
        return "No image URL provided."
    if requests and url.startswith(("http://", "https://")):
        try:
            resp = requests.get(url, timeout=10, stream=True)
            resp.raise_for_status()
            ct = (resp.headers.get("Content-Type") or "").lower()
            if "image/" in ct and len(resp.content) < 10 * 1024 * 1024:
                b64 = image_to_preview_base64(resp.content, max_width=600)
                if b64:
                    emit_live_view_preview(
                        "Image",
                        tool_name="view_image",
                        preview_image_base64=b64,
                        url=url,
                    )
        except Exception:
            pass
    return (
        f"Image available at: {url}. "
        "If this image was attached by the user in this conversation, you already have it in context — describe what you see and respond."
    )


__all__ = [
    "get_clipboard_text",
    "set_clipboard_text",
    "get_clipboard_image",
    "copy_image_to_clipboard",
    "copy_image_from_url_to_clipboard",
    "copy_current_clipboard_image_again",
    "view_image",
]
