import os
import time
import uuid
from dataclasses import dataclass
from pathlib import Path
from typing import Optional

from core.paths import get_path_manager


@dataclass
class TruncateResult:
    preview: str
    truncated: bool
    output_path: Optional[str] = None
    original_bytes: int = 0
    preview_bytes: int = 0


def truncate_and_spill(
    content: str,
    *,
    tool_name: str,
    max_bytes: int = 50 * 1024,
    max_lines: int = 2000,
) -> TruncateResult:
    """Truncate tool output and spill full content to `.dundun/tool-output/`.

    This follows opencode-like behavior:
    - return a preview to the LLM (bounded by bytes/lines)
    - store full output on disk for later inspection
    """

    if content is None:
        content = ""

    # Fast path: small enough
    original_bytes = len(content.encode("utf-8", errors="ignore"))
    if original_bytes <= max_bytes and content.count("\n") + 1 <= max_lines:
        return TruncateResult(
            preview=content,
            truncated=False,
            original_bytes=original_bytes,
            preview_bytes=original_bytes,
        )

    # Preview: enforce max_lines first, then max_bytes.
    lines = content.splitlines()
    preview_lines = lines[:max_lines]
    preview = "\n".join(preview_lines)

    preview_bytes = len(preview.encode("utf-8", errors="ignore"))
    if preview_bytes > max_bytes:
        # Byte-budget truncation (keep prefix only)
        encoded = preview.encode("utf-8", errors="ignore")
        preview = encoded[:max_bytes].decode("utf-8", errors="ignore")
        preview_bytes = len(preview.encode("utf-8", errors="ignore"))

    # Spill full content to disk
    pm = get_path_manager()
    out_dir: Path = pm.get_data_dir() / "tool-output"
    out_dir.mkdir(parents=True, exist_ok=True)

    ts = time.strftime("%Y%m%d_%H%M%S")
    rand = uuid.uuid4().hex[:8]
    filename = f"tool_{tool_name}_{ts}_{rand}.txt"
    out_path = out_dir / filename
    out_path.write_text(content, encoding="utf-8", errors="ignore")

    # Return preview with guidance
    suffix = (
        "\n\n"
        "... (tool output truncated)\n"
        f"Full output saved to: {out_path}\n"
        "Tip: narrow the query (more specific path/pattern) or use read with start_line/end_line."
    )

    return TruncateResult(
        preview=preview + suffix,
        truncated=True,
        output_path=str(out_path),
        original_bytes=original_bytes,
        preview_bytes=preview_bytes,
    )
