"""
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 AptQuery(Tool):
    """Tool to query the APT package database (read-only)."""

    name = "apt_query"
    description = (
        "Query the APT package database (read-only). Useful for inspecting "
        "package metadata, versions, and dependency relationships in the "
        "Debian archive. Supported operations: 'show' (apt-cache show: package "
        "metadata), 'policy' (apt-cache policy: installed/candidate versions "
        "and priorities), 'depends' (apt-cache depends: dependencies), "
        "'rdepends' (apt-cache rdepends: reverse dependencies), 'search' "
        "(apt-cache search: full-text search by regex over names and "
        "descriptions), 'madison' (apt-cache madison: available versions per "
        "suite), 'file-search' (apt-file search: which package provides a "
        "given file path; requires apt-file), 'file-list' (apt-file list: "
        "files shipped by a package; requires apt-file)."
    )

    OPERATIONS = {
        "show": ["apt-cache", "show"],
        "policy": ["apt-cache", "policy"],
        "depends": ["apt-cache", "depends"],
        "rdepends": ["apt-cache", "rdepends"],
        "search": ["apt-cache", "search"],
        "madison": ["apt-cache", "madison"],
        "file-search": ["apt-file", "search"],
        "file-list": ["apt-file", "list"],
    }

    def summary(self, args_dict: dict) -> str:
        return f"apt {args_dict.get('operation', '')} {args_dict.get('argument', '')}".rstrip()

    def __call__(
        self,
        operation: str,
        argument: str,
        extra_args: str = None,
        max_output_chars: int = 6000,
    ) -> str:
        try:
            if operation not in self.OPERATIONS:
                return (
                    f"Error: unknown operation '{operation}'. "
                    f"Valid operations: {', '.join(self.OPERATIONS)}."
                )
            if not argument or not argument.strip():
                return "Error: 'argument' must not be empty."

            cmd = list(self.OPERATIONS[operation])
            if extra_args:
                cmd.extend(shlex.split(extra_args))
            cmd.append(argument)

            try:
                result = subprocess.run(
                    cmd,
                    env={**os.environ, "LC_ALL": "C"},
                    capture_output=True,
                    text=True,
                )
            except FileNotFoundError:
                return (
                    f"Error: '{cmd[0]}' is not installed. "
                    "Install the 'apt-file' package for file-search / "
                    "file-list operations."
                )

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

            limit = int(max_output_chars) if max_output_chars else 6000
            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": {
            "operation": {
                "type": "string",
                "enum": [
                    "show",
                    "policy",
                    "depends",
                    "rdepends",
                    "search",
                    "madison",
                    "file-search",
                    "file-list",
                ],
                "description": "The query operation to perform.",
            },
            "argument": {
                "type": "string",
                "description": "The package name, search pattern, or file path to "
                "query, depending on the operation.",
            },
            "extra_args": {
                "type": "string",
                "description": "Additional arguments passed directly to the "
                "underlying apt-cache/apt-file command.",
            },
            "max_output_chars": {
                "type": "integer",
                "description": "Maximum number of characters to return before "
                "truncating the output. Defaults to 6000.",
            },
        },
        "required": ["operation", "argument"],
    }
