#!/usr/bin/env python3

import os
from typing import List

from ..common import (
    MAX_LINE_LENGTH,
    MAX_LINES_TO_READ,
    MAX_OUTPUT_SIZE,
    normalize_file_path,
)
from ..git_query import find_git_root
from ..mcp import mcp
from ..rules import get_applicable_rules_content
from .commit_utils import append_commit_hash

__all__ = [
    "read_file",
]


@mcp.tool()
async def read_file(
    path: str,
    offset: int | None = None,
    limit: int | None = None,
    chat_id: str | None = None,
    commit_hash: str | None = None,
) -> str:
    """Reads a file from the local filesystem. The path parameter must be an absolute path, not a relative path.
    By default, it reads up to 1000 lines starting from the beginning of the file. You can optionally specify a
    line offset and limit (especially handy for long files), but it's recommended to read the whole file by not
    providing these parameters. Any lines longer than 1000 characters will be truncated. For image files, the
    tool will display the image for you.

    Args:
        path: The absolute path to the file to read
        offset: The line number to start reading from (1-indexed)
        limit: The number of lines to read
        chat_id: The unique ID of the current chat session
        commit_hash: Optional Git commit hash for version tracking

    Returns:
        The file content as a string

    """
    # Set default values
    chat_id = "" if chat_id is None else chat_id

    # Normalize the file path
    full_file_path = normalize_file_path(path)

    # Validate the file path
    if not os.path.exists(full_file_path):
        # Try to find a similar file (stub - would need implementation)
        raise FileNotFoundError(f"File does not exist: {path}")

    if os.path.isdir(full_file_path):
        raise IsADirectoryError(f"Path is a directory, not a file: {path}")

    # Check file size before reading
    file_size = os.path.getsize(full_file_path)
    if file_size > MAX_OUTPUT_SIZE and not offset and not limit:
        raise ValueError(
            f"File content ({file_size // 1024}KB) exceeds maximum allowed size ({MAX_OUTPUT_SIZE // 1024}KB). Please use offset and limit parameters to read specific portions of the file."
        )

    # Handle text files - use async file operations with anyio
    from ..async_file_utils import async_readlines

    all_lines = await async_readlines(
        full_file_path, encoding="utf-8", errors="replace"
    )

    # Get total line count
    total_lines = len(all_lines)

    # Handle offset (convert from 1-indexed to 0-indexed)
    line_offset = 0 if offset is None else (offset - 1 if offset > 0 else 0)

    # Apply offset and limit
    if line_offset >= total_lines:
        raise IndexError(
            f"Offset {offset} is beyond the end of the file (total lines: {total_lines})"
        )

    max_lines = MAX_LINES_TO_READ if limit is None else limit
    selected_lines = all_lines[line_offset : line_offset + max_lines]

    # Process lines (truncate long lines)
    processed_lines: List[str] = []
    for line in selected_lines:
        if len(line) > MAX_LINE_LENGTH:
            processed_lines.append(
                line[:MAX_LINE_LENGTH] + "... (line truncated)",
            )
        else:
            processed_lines.append(line)

    # Add line numbers (1-indexed)
    numbered_lines: List[str] = []
    for i, line in enumerate(processed_lines):
        line_number = line_offset + i + 1  # 1-indexed line number
        numbered_lines.append(f"{line_number:6}\t{line.rstrip()}")

    content = "\n".join(numbered_lines)

    # Add a message if we truncated the file
    if line_offset + len(processed_lines) < total_lines:
        content += f"\n... (file truncated, showing {len(processed_lines)} of {total_lines} lines)"

    # Apply relevant cursor rules
    # Find git repository root
    repo_root = find_git_root(os.path.dirname(full_file_path))

    if repo_root:
        # Add applicable rules content
        content += get_applicable_rules_content(repo_root, full_file_path)

    # Append commit hash
    result, _ = await append_commit_hash(content, full_file_path, commit_hash)
    return result
