"""
neurocli agent — core agentic loop
"""
from .models import call_ai
from .tools import read_file, write_file, list_files, search_files, run_command, git_commit
from .context import get_project_context
from .memory import get_memory_context
from .session import add_message, track


SYSTEM_PROMPT_TEMPLATE = """You are neurocli, an elite AI coding agent running in the terminal.
You help developers write, fix, edit, and understand code.

You have access to tools: read_file, write_file, list_files, search_files, run_command, git_commit.

Rules:
- Always read relevant files BEFORE making changes
- Make precise, minimal edits — don't rewrite what works
- After writing files, briefly explain what you changed and why
- For git commits, use conventional commit format (feat:, fix:, refactor:, etc.)
- If you're unsure, ask the user before acting
- Never touch .env or secrets files

{memory}

{context}
"""


def build_system_prompt(project: str, cwd: str) -> str:
    memory = get_memory_context(project)
    context = get_project_context(cwd)
    return SYSTEM_PROMPT_TEMPLATE.format(memory=memory, context=context)


def run_agent(
    user_input: str,
    messages: list,
    cfg: dict,
    session_id: int,
    project: str,
    cwd: str,
    dry_run: bool = False,
    on_text=None,
    on_tool_start=None,
    on_tool_result=None,
    max_iterations: int = 10
) -> dict:
    """
    Main agentic loop. Returns final result dict.
    Callbacks:
      on_text(text)          — called when AI produces text
      on_tool_start(name, args) — called before tool executes
      on_tool_result(name, result) — called after tool executes
    """
    system = build_system_prompt(project, cwd)
    messages = list(messages)  # copy
    messages.append({"role": "user", "content": user_input})
    add_message(session_id, "user", user_input)

    final_text = ""
    used_tools = []

    for iteration in range(max_iterations):
        response = call_ai(messages, system, cfg, stream_cb=on_text)
        text = response.get("text", "")
        tool_calls = response.get("tool_calls", [])
        model_used = response.get("model_used", "unknown")

        if text:
            final_text = text

        # no more tool calls → done
        if not tool_calls:
            add_message(session_id, "assistant", text or "(tool actions completed)")
            break

        # execute each tool call
        assistant_content = text or ""
        tool_results_content = []

        for tc in tool_calls:
            name = tc["name"]
            args = tc.get("args", {})
            used_tools.append(name)

            if on_tool_start:
                on_tool_start(name, args)

            result = _dispatch_tool(name, args, cfg, session_id, dry_run, cwd)

            if on_tool_result:
                on_tool_result(name, result)

            # format result for next AI message
            result_str = _format_result(name, result)
            tool_results_content.append(result_str)

        # add assistant turn + tool results to history
        messages.append({"role": "assistant", "content": assistant_content + "\n" + "\n".join(
            f"[Tool: {tc['name']}({tc.get('args', {})})]" for tc in tool_calls
        )})
        messages.append({
            "role": "user",
            "content": "Tool results:\n" + "\n---\n".join(tool_results_content)
        })

    return {
        "text": final_text,
        "used_tools": used_tools,
        "model_used": model_used,
        "messages": messages,
    }


def _dispatch_tool(name: str, args: dict, cfg: dict, session_id: int, dry_run: bool, cwd: str) -> dict:
    track("ai_requests", 0)  # already tracked in models

    if name == "read_file":
        path = args.get("path", "")
        return read_file(path, cfg)

    elif name == "write_file":
        path = args.get("path", "")
        content = args.get("content", "")
        return write_file(path, content, cfg, session_id, dry_run)

    elif name == "list_files":
        directory = args.get("directory", cwd)
        return list_files(directory, cfg)

    elif name == "search_files":
        pattern = args.get("pattern", "")
        directory = args.get("directory", cwd)
        return search_files(pattern, directory, cfg)

    elif name == "run_command":
        cmd = args.get("cmd", "")
        return run_command(cmd, cfg, session_id, dry_run)

    elif name == "git_commit":
        message = args.get("message", "chore: update")
        return git_commit(message, cfg, session_id, dry_run)

    else:
        return {"ok": False, "error": f"Unknown tool: {name}"}


def _format_result(name: str, result: dict) -> str:
    if not result.get("ok"):
        return f"[{name}] ERROR: {result.get('error', 'unknown error')}"

    if result.get("dry_run"):
        if name == "write_file":
            return f"[{name}] DRY RUN — would write to {result.get('path')}\nDiff:\n{result.get('diff', '')}"
        if name == "run_command":
            return f"[{name}] DRY RUN — would run: {result.get('cmd')}"
        if name == "git_commit":
            return f"[{name}] DRY RUN — would commit: {result.get('message')}"

    if name == "read_file":
        content = result.get("content", "")
        if len(content) > 3000:
            content = content[:3000] + "\n...[truncated]"
        return f"[read_file: {result.get('path')}]\n{content}"

    if name == "write_file":
        return f"[write_file] ✅ Written to {result.get('path')} ({result.get('lines', 0)} lines)\nDiff:\n{result.get('diff', '')}"

    if name == "list_files":
        return f"[list_files]\n{result.get('tree', '')}"

    if name == "search_files":
        hits = result.get("results", [])
        if not hits:
            return "[search_files] No matches found."
        lines = [f"  {h['file']}:{h['line']} → {h['text']}" for h in hits[:20]]
        return "[search_files] Matches:\n" + "\n".join(lines)

    if name == "run_command":
        out = result.get("stdout", "") or ""
        err = result.get("stderr", "") or ""
        rc = result.get("returncode", 0)
        return f"[run_command] exit={rc}\n{out}{err}"

    if name == "git_commit":
        return f"[git_commit] ✅ {result.get('output', 'committed')}"

    return f"[{name}] {result}"
