"""
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 DpkgQuery(Tool):
    """Tool to query the local dpkg database and .deb files (read-only)."""

    name = "dpkg_query"
    description = (
        "Query the local dpkg database and .deb files (read-only). Useful for "
        "inspecting installed packages on the current system. Supported "
        "operations: 'list-files' (dpkg-query -L: files installed by a "
        "package), 'search' (dpkg-query -S: which installed package owns a "
        "given file path), 'status' (dpkg-query -s: status and metadata of an "
        "installed package), 'list' (dpkg -l: list installed packages, "
        "optionally filtered by a glob pattern), 'contents' (dpkg-deb -c: list "
        "the contents of a .deb file given its path)."
    )

    OPERATIONS = {
        "list-files": (["dpkg-query", "-L"], True),
        "search": (["dpkg-query", "-S"], True),
        "status": (["dpkg-query", "-s"], True),
        "list": (["dpkg", "-l"], False),
        "contents": (["dpkg-deb", "-c"], True),
    }

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

    def __call__(
        self, operation: str, argument: 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)}."
                )

            base, arg_required = self.OPERATIONS[operation]
            if arg_required and (not argument or not argument.strip()):
                return f"Error: operation '{operation}' requires an 'argument'."

            if operation == "contents":
                argument = os.path.expanduser(argument)

            cmd = list(base)
            if argument and argument.strip():
                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."

            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": ["list-files", "search", "status", "list", "contents"],
                "description": "The query operation to perform.",
            },
            "argument": {
                "type": "string",
                "description": "The package name, file path, or glob pattern to "
                "query. Optional only for 'list'; required for all "
                "other operations.",
            },
            "max_output_chars": {
                "type": "integer",
                "description": "Maximum number of characters to return before "
                "truncating the output. Defaults to 6000.",
            },
        },
        "required": ["operation"],
    }
