#!/usr/bin/env python3
# Copyright (c) 2026 PULSE Contributors. Proprietary. All rights reserved.
"""
PULSE Boot Script: Standardized agent initialization.

Every agent runs this on boot. Produces:
1. Registers agent in the swarm
2. Loads full Constitution + Laws (governance is non-negotiable)
3. Checks task board, optionally auto-claims a matching task
4. Searches brain for relevant anti-patterns and knowledge
5. Outputs a standardized status card (same format, every agent)

Usage:
    python pulse_boot.py --agent claude --tier premium [--claim]
    python pulse_boot.py --agent gemini --tier standard
    python pulse_boot.py --reprocess-logs  (retroactive knowledge extraction)

Task board logic delegated to boot_tasks.py (split for Law 7).
Content building delegated to boot_briefing.py (split for Law 7).
Dependencies: Python stdlib + brain_utils, boot_tasks, boot_briefing (internal)
"""
import argparse
import io
import json
import os
import sys
from datetime import datetime, timezone
from pathlib import Path

# Windows Unicode fix — only when running as main script
if os.name == 'nt' and __name__ == '__main__':
    sys.stdout = io.TextIOWrapper(sys.stdout.buffer, encoding='utf-8')
    sys.stderr = io.TextIOWrapper(sys.stderr.buffer, encoding='utf-8')

SCRIPT_DIR = Path(__file__).resolve().parent
ROOT_DIR = SCRIPT_DIR.parent.parent

# When run directly, ensure pulse package is importable
if str(ROOT_DIR) not in sys.path:
    sys.path.insert(0, str(ROOT_DIR))

from pulse.core.brain_utils import (
    load_json_dict, save_json,
    AGENT_REGISTRY_FILE, HIPPOCAMPUS_DIR,
    now_iso as _now_iso,
)
from pulse.brain.boot_briefing import (
    load_recent_brain, load_relevant_brain, search_brain_for_task,
    load_daemon_injections, build_brain_briefing, load_curriculum,
)
from pulse.brain.boot_formatting import build_status_card, build_governance_context
# Re-export task board functions for backward compat
from pulse.brain.boot_tasks import (  # noqa: F401
    get_task_board, find_matching_task, claim_task, _AGENT_MAP,
)

def auto_detect_tier(model_name: str) -> str:
    """Auto-detect tier from model name using substring matching."""
    name = model_name.lower()
    premium = ['opus', 'o3', 'gpt-5', 'gpt-4', 'gemini-3-pro', 'gemini-2.5-pro',
               'grok-4', 'llama3.3:70b', 'codex-5']
    for p in premium:
        if p in name:
            return "premium"
    standard = ['sonnet', 'gpt-4o', 'o4-mini', 'gemini-2.5-flash',
                'deepseek-r1', 'qwen2.5:32b', 'mistral-large']
    for s in standard:
        if s in name:
            return "standard"
    fast = ['haiku', 'flash-lite', 'gpt-4o-mini', 'codex-mini', 'gemini-3-flash',
            'gemini-2.5-flash-lite', 'mistral-small', 'phi3', 'mistral:7b']
    for f in fast:
        if f in name:
            return "fast"
    return "standard"

def register_agent(agent_name: str, tier: str) -> dict:
    """Register or update agent in the registry."""
    mapped = _AGENT_MAP.get(agent_name.lower())
    if mapped:
        agent_id = mapped
        agent_type = "agent"
    else:
        agent_id = f"{agent_name.lower()}_daemon"
        agent_type = "daemon"
    registry = load_json_dict(AGENT_REGISTRY_FILE)
    now = _now_iso()

    if agent_id not in registry:
        registry[agent_id] = {
            "agent_id": agent_id, "model": agent_name, "tier": tier,
            "type": agent_type, "status": "booting", "capabilities": [],
            "last_heartbeat": now, "current_task": None,
            "history": {"tasks_completed": 0, "domains": {}},
        }
    else:
        registry[agent_id]["last_heartbeat"] = now
        registry[agent_id]["status"] = "booting"
        registry[agent_id]["tier"] = tier
        if "type" not in registry[agent_id]:
            registry[agent_id]["type"] = agent_type

    save_json(AGENT_REGISTRY_FILE, registry)
    return registry[agent_id]

def boot(agent_name: str, tier: str, do_claim: bool = False,
         task_query: str = "") -> str:
    """Full boot sequence. Returns context string for agent injection."""
    output_parts = []

    if not tier or tier.lower() == 'auto':
        tier = auto_detect_tier(agent_name)

    # 1. Register
    register_agent(agent_name, tier)

    # 2. IMPERATIVE: Load brain knowledge
    if task_query:
        brain_data = load_relevant_brain(task_query)
    else:
        brain_data = load_recent_brain()

    # 3. Check task board
    tasks = get_task_board()
    matched_task = find_matching_task(tasks, tier) if tasks else {}

    # 4. Auto-claim if requested
    if do_claim and matched_task:
        if not claim_task(matched_task, agent_name):
            matched_task = {}

    # 5. Task-specific brain search
    task_title = matched_task.get("title", "") if matched_task else ""
    task_search = search_brain_for_task(task_title)

    # 5b. D4+D5+D6+U7: Inject daemon state + cross-agent intel
    injections = load_daemon_injections(agent_name=agent_name)
    brain_data["synthetic_constraints"] = injections["synthetic_constraints"]
    brain_data["gate_feedback"] = injections["gate_feedback"]
    brain_data["golden_paths"] = injections["golden_paths"]
    brain_data["cross_agent_intel"] = injections["cross_agent_intel"]

    # 5c. D15: Inject domain curriculum
    task_domain = matched_task.get("domain", "") if matched_task else ""
    curriculum = load_curriculum(domain=task_domain, task_query=task_title or task_query)
    if curriculum:
        brain_data["curriculum"] = curriculum

    # 6. Status card (compact — user sees this)
    output_parts.append(build_status_card(agent_name, tier, matched_task, task_search))

    # 7. MANDATORY BRAIN BRIEFING (agent sees this — cannot skip)
    output_parts.append(build_brain_briefing(brain_data, task_search))

    # 8. Governance context
    output_parts.append(build_governance_context())

    # 9. Open tasks summary
    if tasks and not matched_task:
        output_parts.append("## OPEN TASKS (unclaimed)")
        for t in tasks[:10]:
            rec = t.get("recommended_tier", "?")
            output_parts.append(f"  [{rec}] {t.get('title', '?')} ({t.get('id', '?')})")
    # 10. Instructions
    output_parts.append("## AGENT INSTRUCTIONS")
    if matched_task:
        output_parts.append(f"You have claimed task: {matched_task.get('title', '?')}")
        output_parts.append(f"Task ID: {matched_task.get('id', '?')}")
        output_parts.append(f"Domain: {matched_task.get('domain', '?')}")
        output_parts.append("1. Apply the BRAIN BRIEFING above to this task.")
        output_parts.append("2. Before EVERY action, run: python pulse/brain/brain_query.py --query \"<what you are doing>\"")
        output_parts.append("3. Execute following Constitution and Laws.")
        output_parts.append("4. Output a brief Status Report (name, task, 1-2 brain insights). NO ceremony.")
        output_parts.append("5. Do the work.")
        output_parts.append("6. When done: python pulse/brain/pulse_save.py")
        output_parts.append("7. Decompose subtasks via relay — background workers auto-execute them.")
    else:
        output_parts.append("No matching tasks available. Output a brief Status Report and WAIT.")
        output_parts.append("Do NOT start autonomous work without a task assignment.")
        output_parts.append("Background workers are polling — relay tasks to the board and they'll be claimed automatically.")
    return "\n".join(output_parts)


def main():
    parser = argparse.ArgumentParser(description="PULSE Standardized Agent Boot")
    parser.add_argument("--agent", default="unknown", help="Agent name")
    parser.add_argument("--tier", default="standard", choices=["fast", "standard", "premium"])
    parser.add_argument("--claim", action="store_true", help="Auto-claim a matching task")
    parser.add_argument("--reprocess-logs", action="store_true",
                        help="Retroactive: extract knowledge from all existing logs")
    parser.add_argument("--json", action="store_true", help="Output as JSON")
    args = parser.parse_args()

    if args.reprocess_logs:
        from pulse.brain.reprocess_logs import reprocess_logs
        reprocess_logs()
        return

    result = boot(args.agent, args.tier, args.claim)
    if args.json:
        print(json.dumps({"boot_context": result, "agent": args.agent, "tier": args.tier}))
    else:
        print(result)


if __name__ == "__main__":
    main()
