"""Read file tool with hashline support and image reading.

Hashline format: {line_num}:{hash}|{content}
Example:
    1:a3f|function hello() {
    2:f1b|  return "world";
    3:0e2|}

The hash is a 3-char hex derived from line content, enabling
optimistic locking for edits.
"""

from __future__ import annotations

import base64
import os
import zlib

from krim.tools.base import Tool


def _hash_line(line: str) -> str:
    """Generate 3-char hex hash from line content."""
    return format(zlib.crc32(line.encode()) & 0xFFF, '03x')

# Marker for image content - agent.py parses this to build image blocks
IMAGE_MARKER = "<<<IMAGE:"

# Supported image extensions and their MIME types
IMAGE_EXTENSIONS = {
    ".png": "image/png",
    ".jpg": "image/jpeg",
    ".jpeg": "image/jpeg",
    ".gif": "image/gif",
    ".webp": "image/webp",
}

MAX_IMAGE_SIZE = 5 * 1024 * 1024  # 5MB for images (API limit considerations)


class ReadTool(Tool):
    name = "read"
    description = (
        "Read a file with hashlines (line:hash|content). Use offset/limit for large files. "
        "Always read before editing. Use the hash references for edit operations. "
        "Supports images (png, jpg, gif, webp) - returns image for visual analysis."
    )
    parameters = {
        "path": {"type": "string", "description": "File path to read"},
        "offset": {"type": "integer", "description": "Start from this line (1-indexed)", "optional": True},
        "limit": {"type": "integer", "description": "Max lines to return", "optional": True},
    }

    MAX_FILE_SIZE = 10 * 1024 * 1024  # 10MB

    def run(self, path: str, offset: int = 1, limit: int = 2000) -> str:
        path = os.path.expanduser(path)
        if not os.path.isfile(path):
            return f"error: {path} not found"

        # Check if it's an image file
        ext = os.path.splitext(path)[1].lower()
        if ext in IMAGE_EXTENSIONS:
            return self._read_image(path, ext)

        # Text file handling
        try:
            size = os.path.getsize(path)
            if size > self.MAX_FILE_SIZE:
                return f"error: {path} is too large ({size // 1024 // 1024}MB). use offset/limit or bash to read portions."
            with open(path, "r") as f:
                lines = f.readlines()

            total = len(lines)
            start = max(0, offset - 1)
            end = min(total, start + limit)
            selected = lines[start:end]

            numbered = []
            for i, line in enumerate(selected, start=start + 1):
                content = line.rstrip()
                h = _hash_line(content)
                numbered.append(f"{i}:{h}|{content}")

            result = "\n".join(numbered)
            if end < total:
                result += f"\n... ({total - end} more lines, {total} total)"

            return result
        except UnicodeDecodeError:
            return f"error: {path} is a binary file"
        except Exception as e:
            return f"error: {e}"

    def _read_image(self, path: str, ext: str) -> str:
        """Read image file and return as base64 with marker."""
        try:
            size = os.path.getsize(path)
            if size > MAX_IMAGE_SIZE:
                return f"error: image {path} is too large ({size // 1024 // 1024}MB, max 5MB)"

            with open(path, "rb") as f:
                data = f.read()

            b64 = base64.b64encode(data).decode("ascii")
            media_type = IMAGE_EXTENSIONS[ext]

            # Format: <<<IMAGE:media_type:base64_data>>>
            return f"{IMAGE_MARKER}{media_type}:{b64}>>>"
        except Exception as e:
            return f"error: failed to read image {path}: {e}"
