"""Scoped code/accessibility review agents."""

from typing import Dict, List

from backend import config
from backend.agents.base import AgentResult, AgentScope, AgentUsage, failure
from backend.llm import call_json
from backend.pipeline import code_refs
from backend.pipeline._serialize import dumps, slim_sheets
from backend.pipeline._stage import collect_list, validate_issue
from backend.pipeline.jurisdiction import active_review_paths
from backend.prompts import CODE_REVIEW_SYSTEM_PROMPT, CODE_REVIEW_USER_INSTRUCTION


def build_code_scopes(
    sheets: List[Dict], jurisdiction: Dict, sheet_index: Dict
) -> List[AgentScope]:
    cap = max(1, config.AGENT_CODE_BATCH_SIZE)
    fragments: List[Dict] = []
    for sheet in sheets:
        assertions = sheet.get("assertions") or []
        if not assertions:
            continue
        for offset in range(0, len(assertions), cap):
            fragments.append({
                **sheet,
                "assertions": assertions[offset:offset + cap],
            })
    batches: List[List[Dict]] = []
    current: List[Dict] = []
    count = 0
    for fragment in fragments:
        size = len(fragment["assertions"])
        if current and count + size > cap:
            batches.append(current)
            current, count = [], 0
        current.append(fragment)
        count += size
    if current:
        batches.append(current)
    return [
        AgentScope(
            scope_id=f"code:{index + 1}",
            payload={
                "sheets": batch,
                "jurisdiction": jurisdiction,
                "sheet_index": sheet_index,
            },
        )
        for index, batch in enumerate(batches)
    ]


class CodeAgent:
    name = "code_reviewer"

    def __init__(self, usage: AgentUsage) -> None:
        self.usage = usage

    def run(self, scope: AgentScope) -> AgentResult:
        try:
            sheets = scope.payload.get("sheets") or []
            jurisdiction = scope.payload.get("jurisdiction") or {}
            sheet_index = scope.payload.get("sheet_index") or {}
            assertions = [
                assertion for sheet in sheets
                for assertion in sheet.get("assertions", [])
            ]
            excerpts = code_refs.retrieve(
                active_review_paths(jurisdiction), assertions
            )
            instruction = CODE_REVIEW_USER_INSTRUCTION
            for key, value in {
                "jurisdiction": dumps(jurisdiction),
                "sheet_index": dumps(sheet_index),
                "assertions": dumps(slim_sheets(sheets)),
                "code_references": code_refs.format_excerpts(excerpts),
            }.items():
                instruction = instruction.replace("{" + key + "}", value)
            parsed = call_json(
                system_prompt=CODE_REVIEW_SYSTEM_PROMPT,
                user_text=instruction,
                max_tokens=config.CODE_MAX_TOKENS,
                model=config.AGENT_CODE_MODEL,
                usage_tracker=self.usage,
                usage_stage="agent.code",
            )
            findings = collect_list(
                parsed, "issues", lambda item: validate_issue(item, "code")
            )
            for finding in findings:
                finding.update(agent=self.name, scope_id=scope.scope_id)
            return AgentResult(scope_id=scope.scope_id, artifacts=findings)
        except Exception as exc:
            return failure(scope, exc)
