"""
ShelfAI Chunker — Splits monolithic agent files into modular chunks.

Two-layer approach:
1. Heuristic pre-filter (free): Extracts soul/rules/read-order into always-loaded chunks.
2. LLM semantic pass (~$0.01/agent): Groups remaining sections by deliverable/workflow.

The pre-filter handles ~35% of the work reliably. The LLM handles semantic
grouping that requires understanding the agent's workflow.
"""

from pathlib import Path
from dataclasses import dataclass, field


@dataclass
class ChunkCandidate:
    """An agent file that should be chunked."""
    path: Path
    line_count: int
    agent_name: str


@dataclass
class ChunkReview:
    """An existing chunk flagged for review."""
    agent_dir: str
    chunk_name: str
    line_count: int
    reason: str  # "suggest review" or "suggest merge"


@dataclass
class ScanResult:
    """Result of scanning for chunk candidates."""
    candidates: list[ChunkCandidate] = field(default_factory=list)
    reviews: list[ChunkReview] = field(default_factory=list)
    ok_count: int = 0


@dataclass
class ChunkResult:
    """Result of chunking an agent file."""
    chunks: dict  # name -> {"class": str, "content": str, "lines": int}
    router: str
    original_lines: int
    always_lines: int
    savings_pct: int


# ── Scanning ─────────────────────────────────────────────────────────────────

def scan_agents(
    agents_dir: Path,
    min_lines: int = 150,
    review_threshold: int = 80,
) -> ScanResult:
    """
    Scan an agents directory for chunking candidates and oversized chunks.

    Args:
        agents_dir: Path to the agents/ directory
        min_lines: Minimum lines to flag an unchunked agent
        review_threshold: Flag existing chunks above this line count for LLM review
    """
    result = ScanResult()

    if not agents_dir.exists():
        return result

    for agent_dir in sorted(agents_dir.iterdir()):
        if not agent_dir.is_dir():
            continue

        agent_md = agent_dir / "AGENT.md"
        chunks_dir = agent_dir / "chunks"

        if not agent_md.exists():
            continue

        line_count = sum(1 for _ in agent_md.open())

        # Not yet chunked and over threshold
        if not chunks_dir.exists() and line_count > min_lines:
            # Extract agent name from first line
            first_line = agent_md.read_text().split("\n", 1)[0]
            name = first_line.lstrip("# ").strip()
            result.candidates.append(ChunkCandidate(
                path=agent_md,
                line_count=line_count,
                agent_name=name,
            ))

        # Already chunked — flag for LLM review (LLM has latitude to say "no change")
        if chunks_dir.exists():
            for chunk in chunks_dir.glob("*.md"):
                chunk_lines = sum(1 for _ in chunk.open())
                if chunk_lines > review_threshold:
                    result.reviews.append(ChunkReview(
                        agent_dir=agent_dir.name,
                        chunk_name=chunk.name,
                        line_count=chunk_lines,
                        reason="suggest review",
                    ))
                elif chunk_lines < 10:
                    result.reviews.append(ChunkReview(
                        agent_dir=agent_dir.name,
                        chunk_name=chunk.name,
                        line_count=chunk_lines,
                        reason="suggest merge",
                    ))
            if not any(r.agent_dir == agent_dir.name for r in result.reviews):
                result.ok_count += 1

    return result


# ── Heuristic Pre-filter ─────────────────────────────────────────────────────

def prefilter(filepath: Path) -> ChunkResult:
    """
    Run the heuristic pre-filter on a monolithic AGENT.md.

    Extracts soul/rules/read-order as always-loaded chunks.
    Remaining sections are left as a single "remaining.md" for the
    LLM semantic pass to group properly.

    This is the safe, free first pass that can't do damage.
    """
    text = filepath.read_text()
    lines = text.split("\n")
    original_lines = len(lines)

    preamble, sections = _parse_h2_sections(text)
    chunks = {}

    # Files without H2 sections should still be preserved as a task chunk,
    # not silently dropped or incorrectly treated as "always loaded".
    if not sections:
        chunks["remaining"] = {
            "class": "task",
            "content": text,
            "lines": original_lines,
            "sections": [],
        }
        router = _generate_router(chunks)
        return ChunkResult(
            chunks=chunks,
            router=router,
            original_lines=original_lines,
            always_lines=0,
            savings_pct=max(0, round((1 - 30 / max(original_lines, 1)) * 100)),
        )

    def add(name, cls, content, line_count):
        if name not in chunks:
            chunks[name] = {"class": cls, "content": "", "lines": 0}
        chunks[name]["content"] += content + "\n"
        chunks[name]["lines"] += line_count

    # Preamble -> soul
    if preamble.strip():
        add("soul", "always", preamble, preamble.count("\n") + 1)

    remaining_sections = []

    for section in sections:
        title = section["title"]

        if _is_soul(title):
            add("soul", "always", section["content"], section["lines"])
        elif _is_rules(title):
            add("rules", "always", section["content"], section["lines"])
        elif _is_read_order(title):
            add("read-order", "always", section["content"], section["lines"])
        else:
            remaining_sections.append(section)

    # Bundle remaining sections for LLM pass
    if remaining_sections:
        remaining_content = "\n".join(s["content"] for s in remaining_sections)
        remaining_lines = sum(s["lines"] for s in remaining_sections)
        chunks["remaining"] = {
            "class": "task",
            "content": remaining_content,
            "lines": remaining_lines,
            "sections": remaining_sections,  # Keep parsed sections for LLM
        }

    always_lines = sum(c["lines"] for c in chunks.values() if c["class"] == "always")
    total = sum(c["lines"] for c in chunks.values())
    savings = round((1 - (always_lines + 30) / max(total, 1)) * 100)

    router = _generate_router(chunks)

    return ChunkResult(
        chunks=chunks,
        router=router,
        original_lines=original_lines,
        always_lines=always_lines,
        savings_pct=savings,
    )


# ── Parsing helpers ──────────────────────────────────────────────────────────

def _parse_h2_sections(text: str) -> tuple:
    """Parse text into preamble + list of H2 sections."""
    lines = text.split("\n")
    sections = []
    current_title = None
    current_lines = []
    preamble_lines = []

    for line in lines:
        if line.startswith("## "):
            if current_title is not None:
                sections.append({
                    "title": current_title,
                    "content": "\n".join(current_lines),
                    "lines": len(current_lines),
                })
            else:
                preamble_lines = current_lines[:]
            current_title = line[3:].strip()
            current_lines = [line]
        else:
            current_lines.append(line)

    if current_title is None:
        return "\n".join(current_lines), []

    if current_title is not None:
        sections.append({
            "title": current_title,
            "content": "\n".join(current_lines),
            "lines": len(current_lines),
        })

    return "\n".join(preamble_lines), sections


def _is_soul(title: str) -> bool:
    t = title.lower()
    return any(kw in t for kw in [
        "mission", "pipeline", "role", "identity", "active accounts"
    ])


def _is_rules(title: str) -> bool:
    t = title.lower()
    return any(kw in t for kw in ["rules", "guidelines", "constraints"])


def _is_read_order(title: str) -> bool:
    t = title.lower()
    return any(kw in t for kw in [
        "system integration", "read order", "read-order", "data source"
    ])


def _generate_router(chunks: dict) -> str:
    lines = [
        "> **Auto-chunked by ShelfAI.** Load only what you need.",
        "",
        "## Always Load (every run)",
        "",
    ]
    step = 1
    for slug in ["soul", "rules", "read-order"]:
        if slug in chunks:
            lines.append(f"{step}. `chunks/{slug}.md`")
            step += 1
    lines.append(f"{step}. `MEMORY.md`")
    lines.append("")

    task_chunks = {
        s: c for s, c in chunks.items()
        if c["class"] != "always" and not s.startswith("_")
    }
    if task_chunks:
        lines.append("## Load by Task")
        lines.append("")
        lines.append("| Task | Load |")
        lines.append("|------|------|")
        for slug, chunk in task_chunks.items():
            label = slug.replace("-", " ").title()
            lines.append(f"| {label} | `chunks/{slug}.md` |")
        lines.append("")

    return "\n".join(lines)
