"""
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 FileReader(Tool):
    """Tool to read file contents, optionally within a line number range."""

    name = "file_reader"
    description = (
        "Read the contents of a file. Optionally specify "
        "start_line and/or end_line (1-indexed, inclusive) "
        "to read only a range of lines."
    )

    def summary(self, args_dict: dict) -> str:
        path = args_dict.get("path", "")
        start, end = args_dict.get("start_line"), args_dict.get("end_line")
        if start is not None or end is not None:
            return f"read {path}:{start or 1}-{end or ''}"
        return f"read {path}"

    def __call__(self, path: str, start_line: int = None, end_line: int = None) -> str:
        try:
            path = os.path.expanduser(path)
            if not os.path.isfile(path):
                return f"Error: '{path}' is not a file or does not exist."

            with open(path, "r") as f:
                lines = f.readlines()

            total = len(lines)

            # Validate / default the range (1-indexed, inclusive)
            if start_line is not None:
                start_line = int(start_line)
                if start_line < 1:
                    return "Error: start_line must be >= 1."
            else:
                start_line = 1

            if end_line is not None:
                end_line = int(end_line)
                if end_line < start_line:
                    return "Error: end_line must be >= start_line."
            else:
                end_line = total

            if start_line > total:
                return (
                    f"Error: start_line ({start_line}) is beyond the "
                    f"end of the file ({total} lines)."
                )

            # Clamp end_line to the actual file length
            end_line = min(end_line, total)

            selected = lines[start_line - 1 : end_line]
            numbered = [
                f"{start_line + i}: {line.rstrip()}" for i, line in enumerate(selected)
            ]
            header = (
                f"File: {path}  (showing lines {start_line}-{end_line}" f" of {total})"
            )
            return header + "\n" + "\n".join(numbered)
        except Exception as e:
            return f"Error: {str(e)}"

    parameters = {
        "type": "object",
        "properties": {
            "path": {
                "type": "string",
                "description": "Absolute or relative path to " "the file to read.",
            },
            "start_line": {
                "type": "integer",
                "description": "First line to read (1-indexed, "
                "inclusive). Defaults to 1.",
            },
            "end_line": {
                "type": "integer",
                "description": "Last line to read (1-indexed, "
                "inclusive). Defaults to the last "
                "line of the file.",
            },
        },
        "required": ["path"],
    }
