#!/usr/bin/env python3
# ollie:parallel read
# ollie:tier cold
# ollie:prompt
# ## file_read
#
# Read a file with line numbers. Prefer segments over full reads.
#
# **Args**: `[path]` or `[path, start, end]` or `[path, "--head=N"]`
#
# - `start`/`end`: 1-indexed line numbers, inclusive. Must be strings. Max segment: 2000 lines.
# - No start/end: reads up to 500 lines from line 1.
# - `--head=N`: read first N lines.
#
# ```
# call_tool: calls=[{tool: "file_read", args: ["/abs/path"]}]
# call_tool: calls=[{tool: "file_read", args: ["/abs/path", "100", "200"]}]
# call_tool: calls=[{tool: "file_read", args: ["/abs/path", "--head=50"]}]
# ```
#
# - BAD: `args: ["/path", 100, 200]` — numbers are not strings
# - BAD: `args: ["/path", "match=foo"]` — not a valid argument
# - GOOD: `args: ["/abs/path", "100", "200"]` — string line numbers
#
# **Constraints**: Absolute paths only.
# ollie:end

import sys
import os
from itertools import islice

MAX_LINES = 500       # default window for whole-file reads (no start/end)
MAX_SEGMENT = 2000    # max window when start/end are given
MAX_LINE_LEN = 2000

# Parse --head=N from args
head = None
args = []
for a in sys.argv[1:]:
    if a.startswith('--head='):
        head = int(a.split('=', 1)[1])
    else:
        args.append(a)

if not args:
    print("usage: file_read <file_path> [start] [end] [--head=N]")
    sys.exit(1)

file_path = args[0]
start = int(args[1]) if len(args) > 1 else 1
end   = int(args[2]) if len(args) > 2 else None

if not os.path.isabs(file_path):
    print(f"STATUS=error")
    print(f"error: path must be absolute, got: {file_path}")
    sys.exit(1)

if not os.path.exists(file_path):
    print("STATUS=not_found")
    print("(new file)")
    sys.exit(0)

if not os.path.isfile(file_path):
    print(f"STATUS=error")
    print(f"error: not a regular file: {file_path}")
    sys.exit(1)

with open(file_path, 'rb') as f:
    raw_head = f.read(8192)
    if b'\x00' in raw_head:
        # Binary file -- try to detect image type
        IMAGE_SIGS = [
            b'\x89PNG\r\n\x1a\n', b'\xff\xd8\xff',
            b'GIF87a', b'GIF89a', b'RIFF',
        ]
        is_image = any(raw_head.startswith(s) for s in IMAGE_SIGS)
        if is_image:
            print("STATUS=ok BINARY=true IMAGE=true")
            print("(image file — use image_read for visual content)")
        else:
            print("STATUS=ok BINARY=true")
            print("(binary file)")
        sys.exit(0)

# Count total lines
try:
    with open(file_path, 'r', encoding='utf-8') as f:
        total_lines = sum(1 for _ in f)
except UnicodeDecodeError:
    with open(file_path, 'r', encoding='utf-8', errors='replace') as f:
        total_lines = sum(1 for _ in f)

start = max(1, start)
if head:
    end = start + head - 1
elif end is None:
    end = start + MAX_LINES - 1
else:
    end = min(end, start + MAX_SEGMENT - 1)

try:
    with open(file_path, 'r', encoding='utf-8') as f:
        for _ in islice(f, start - 1):
            pass
        segment_lines = list(islice(f, end - start + 1))
except UnicodeDecodeError:
    with open(file_path, 'r', encoding='utf-8', errors='replace') as f:
        for _ in islice(f, start - 1):
            pass
        segment_lines = list(islice(f, end - start + 1))

if not segment_lines:
    if start == 1:
        print(f"STATUS=ok LINES=0 TOTAL_LINES={total_lines} TRUNCATED=false EMPTY=true")
        print("(empty file)")
    else:
        print(f"STATUS=ok LINES=0 TOTAL_LINES={total_lines} TRUNCATED=false EMPTY=true")
        print("(no lines in range)")
    sys.exit(0)

out = []
for i, line in enumerate(segment_lines):
    lineno = start + i
    text = line.rstrip('\n')
    if len(text) > MAX_LINE_LEN:
        text = text[:MAX_LINE_LEN] + '...'
    out.append(f"{lineno:>6}\t{text}")

last_line_shown = start + len(out) - 1
truncated = last_line_shown < total_lines
print(f"STATUS=ok LINES={len(out)} TOTAL_LINES={total_lines} TRUNCATED={str(truncated).lower()}")
print('\n'.join(out))
