"""
Copyright (C) 2024-2026 Mo Zhou <lumin@debian.org>

This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU Lesser General Public License for more details.

You should have received a copy of the GNU Lesser General Public License
along with this program.  If not, see <https://www.gnu.org/licenses/>.
"""

import difflib
import os
import pty
import re
import select
import shlex
import subprocess
import tempfile
from collections import deque

from rich.console import Console
from rich.live import Live
from rich.panel import Panel
from rich.text import Text

from .base import Tool, _diff_stat, _short, render_diff


class CurlRunner(Tool):
    """Tool to execute curl commands."""

    name = "curl_runner"
    description = (
        "Execute a curl command to make HTTP requests. Plain GET/HEAD "
        "requests run directly; requests that change server state "
        "(other methods or a request body) or write to the local "
        "filesystem require user confirmation."
    )

    # curl flags that write to the filesystem, upload data, override the
    # method, or read configuration from disk.
    _DANGEROUS_FLAGS = {
        "-o",
        "--output",
        "-O",
        "--remote-name",
        "--remote-name-all",
        "--output-dir",
        "--create-dirs",
        "-D",
        "--dump-header",
        "-T",
        "--upload-file",
        "-K",
        "--config",
        "-X",
        "--request",
    }

    _SAFE_METHODS = {"GET", "HEAD"}

    def _build_cmd(
        self,
        url: str,
        method: str = "GET",
        headers: list = None,
        data: str = None,
        extra_args: str = None,
    ) -> list:
        cmd = ["curl", "-s", "-S"]
        if method != "GET":
            cmd.extend(["-X", method])
        if headers:
            for header in headers:
                cmd.extend(["-H", header])
        if data is not None:
            cmd.extend(["-d", data])
        if extra_args:
            cmd.extend(shlex.split(extra_args))
        cmd.append(url)
        return cmd

    def _risk_reasons(self, args_dict: dict) -> list:
        reasons = []
        method = (args_dict.get("method") or "GET").upper()
        if method not in self._SAFE_METHODS:
            reasons.append(f"HTTP method '{method}' may change server state")
        if args_dict.get("data") is not None:
            reasons.append("request sends a body (-d/--data)")
        extra = args_dict.get("extra_args", "") or ""
        try:
            tokens = shlex.split(extra)
        except ValueError:
            tokens = extra.split()
        found = [t for t in tokens if t in self._DANGEROUS_FLAGS]
        if found:
            reasons.append("dangerous curl flag(s): " + ", ".join(found))
        return reasons

    def needs_confirmation(self, args_dict: dict) -> tuple:
        reasons = self._risk_reasons(args_dict)
        if reasons:
            return True, "; ".join(reasons)
        return False, ""

    def confirmation_context(self, args_dict: dict) -> str:
        cmd = self._build_cmd(
            args_dict.get("url", ""),
            args_dict.get("method", "GET"),
            args_dict.get("headers"),
            args_dict.get("data"),
            args_dict.get("extra_args"),
        )
        return "Command to run:\n" + " ".join(shlex.quote(c) for c in cmd)

    def summary(self, args_dict: dict) -> str:
        return f"curl {args_dict.get('method', 'GET')} {args_dict.get('url', '')}"

    def __call__(
        self,
        url: str,
        method: str = "GET",
        headers: list = None,
        data: str = None,
        max_output_chars: int = 4000,
        extra_args: str = None,
    ) -> str:
        try:
            cmd = self._build_cmd(url, method, headers, data, extra_args)

            result = subprocess.run(cmd, capture_output=True, text=True)

            out = result.stdout
            if result.stderr:
                out += "\nSTDERR:\n" + result.stderr

            limit = int(max_output_chars) if max_output_chars else 4000
            if len(out) > limit:
                half = limit // 2
                out = out[:half] + "\n...(truncated output)...\n" + out[-half:]

            ret = f"Status: {result.returncode}\n"
            if out.strip():
                ret += f"Output:\n{out}\n"
            else:
                ret += "No output.\n"
            return ret
        except Exception as e:
            return f"Error: {str(e)}"

    parameters = {
        "type": "object",
        "properties": {
            "url": {
                "type": "string",
                "description": "The URL to request.",
            },
            "method": {
                "type": "string",
                "description": "The HTTP method to use. Defaults to 'GET'.",
            },
            "headers": {
                "type": "array",
                "items": {"type": "string"},
                "description": "A list of HTTP headers to include (e.g. ['Content-Type: application/json']).",
            },
            "data": {
                "type": "string",
                "description": "Data to send in the request body.",
            },
            "max_output_chars": {
                "type": "integer",
                "description": "Maximum number of characters to "
                "return before truncating the "
                "output. Defaults to 4000.",
            },
            "extra_args": {
                "type": "string",
                "description": "Additional arguments passed directly to curl.",
            },
        },
        "required": ["url"],
    }
