"""
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 SbuildRunner(Tool):
    """Tool to build a Debian package using sbuild."""

    name = "sbuild_runner"
    description = (
        "Builds a Debian package using sbuild. By default it runs "
        "sbuild in the current directory. You should prefer --no-clean-source by default."
    )

    def needs_confirmation(self, args_dict: dict) -> tuple:
        return False, ""

    def summary(self, args_dict: dict) -> str:
        dist = args_dict.get("dist")
        return f"sbuild {args_dict.get('path', '.')}" + (f" ({dist})" if dist else "")

    def __call__(
        self,
        path: str = ".",
        dist: str = None,
        arch: str = None,
        extra_args: str = None,
    ) -> str:
        try:
            path = os.path.expanduser(path)
            if not os.path.exists(path):
                return f"Error: '{path}' does not exist."

            cmd = ["sbuild"]
            if dist:
                cmd.extend(["-d", dist])
            if arch:
                cmd.extend(["--arch", arch])

            if extra_args:
                cmd.extend(shlex.split(extra_args))

            if path != ".":
                cmd.append(path)

            log_fd, log_path = tempfile.mkstemp(prefix="sbuild_", suffix=".log")
            os.close(log_fd)

            master_fd, slave_fd = pty.openpty()

            with open(log_path, "w") as log_file:
                process = subprocess.Popen(
                    cmd,
                    stdout=slave_fd,
                    stderr=slave_fd,
                    stdin=slave_fd,
                    close_fds=True,
                )
                os.close(slave_fd)

                console = Console()
                term_height = console.size.height
                max_lines = max(10, term_height - 2)

                output_buffer = ""
                output_lines = deque(maxlen=max_lines)

                with Live(
                    Panel("Starting sbuild...", title="sbuild", border_style="blue"),
                    refresh_per_second=10,
                    transient=True,
                    console=console,
                ) as live:
                    while True:
                        try:
                            r, _, _ = select.select([master_fd], [], [], 0.1)
                        except Exception:
                            break

                        if master_fd in r:
                            try:
                                chunk = os.read(master_fd, 4096)
                            except OSError:
                                break

                            if not chunk:
                                break

                            chunk_str = chunk.decode("utf-8", errors="replace")
                            log_file.write(chunk_str)
                            log_file.flush()

                            output_buffer += chunk_str
                            lines = output_buffer.split("\n")
                            if len(lines) > 1:
                                for line in lines[:-1]:
                                    line = line.rstrip("\r")
                                    if "\r" in line:
                                        line = line.split("\r")[-1]
                                    output_lines.append(line)
                                output_buffer = lines[-1]

                                display_text = "\n".join(
                                    list(output_lines) + [output_buffer]
                                )
                                live.update(
                                    Panel(
                                        Text.from_ansi(display_text),
                                        title="sbuild",
                                        border_style="blue",
                                    )
                                )
                        elif process.poll() is not None:
                            break

                os.close(master_fd)
                process.wait()

            with open(log_path, "r") as f:
                content = f.read()

            out = content
            if len(out) > 4000:
                out = "...(truncated log)...\n" + out[-4000:]

            ret = f"Status: {process.returncode}\n"
            ret += f"Log path: {log_path}\n"
            if out:
                ret += f"STDOUT:\n{out}\n"
            return ret
        except Exception as e:
            return f"Error: {str(e)}"

    parameters = {
        "type": "object",
        "properties": {
            "path": {
                "type": "string",
                "description": "Path to the package source directory or .dsc file. Defaults to '.'.",
            },
            "dist": {
                "type": "string",
                "description": "Distribution to build for (e.g. 'unstable'). Maps to sbuild -d.",
            },
            "arch": {
                "type": "string",
                "description": "Architecture to build for (e.g. 'amd64'). Maps to sbuild --arch.",
            },
            "extra_args": {
                "type": "string",
                "description": "Additional arguments passed directly to sbuild.",
            },
        },
        "required": [],
    }
