#!/usr/bin/env python3
# Copyright (c) 2026 PULSE Contributors. Proprietary. All rights reserved.
"""
Boot Tasks: Task board loading, matching, and claiming logic.

Extracted from pulse_boot.py per Law 7 (files <= 300 lines).

Functions:
  - get_task_board: Load open tasks from the board
  - find_matching_task: Match a task to agent tier (strict hierarchy)
  - claim_task: Atomically claim a task on the board

Dependencies: Python stdlib + brain_utils (internal)
"""
from datetime import datetime, timezone
from pathlib import Path

import sys

from pulse.core.brain_utils import (
    load_json_dict, atomic_update_json, TASK_BOARD_FILE,
    now_iso as _now_iso,
)

# Agent name mapping (shared with pulse_boot)
# SoC: Daemons must NEVER claim tasks. Only workers claim via claim_task_atomic().
# This map is used ONLY for display/logging — never for task claiming.
_AGENT_MAP = {
    "claude": "claude", "gemini": "gemini",
    "codex": "codex", "grok": "grok",
    "haiku": "claude", "sonnet": "claude",
    "opus": "claude", "flash": "gemini",
}

# Daemon IDs are NEVER valid claimants
_DAEMON_IDS = frozenset({
    "claude_daemon", "gemini_daemon", "codex_daemon",
    "grok_daemon", "bridge_daemon", "neural_daemon",
    "compliance_daemon", "dispatcher_daemon",
})


def get_task_board() -> list:
    """Get all open tasks from the board."""
    board = load_json_dict(TASK_BOARD_FILE)
    tasks = []
    for d in board.get("dispatches", []):
        for t in d.get("tasks", []):
            if t.get("status") == "open":
                tasks.append(t)
    return tasks


def find_matching_task(tasks: list, tier: str) -> dict:
    """Find a task matching the agent's tier (strict hierarchy)."""
    for t in tasks:
        rec_tier = t.get("recommended_tier", "standard")
        if rec_tier == tier:
            return t
    if tier == "premium" and tasks:
        return tasks[0]
    if tier == "standard":
        for t in tasks:
            rec_tier = t.get("recommended_tier", "standard")
            if rec_tier in ("fast", "standard"):
                return t
    return {}


def _is_valid_worker_id(name: str) -> bool:
    """Check if agent_name is a proper worker ID (not a bare provider name).

    Valid: 'claude-sonnet-4-6_01', 'qwen3-coder-next-cloud_03', 'claude_interactive_1234'
    Invalid: 'claude', 'gemini', 'codex', 'ollama-cloud'
    """
    import re
    # Must contain underscore followed by digits (worker index or PID)
    return bool(re.search(r'_\d+', name))


def claim_task(task: dict, agent_name: str) -> bool:
    """Claim a task on the board (atomic via filelock).
    SoC guard: daemon IDs and bare agent names are rejected — only
    properly-identified workers may claim tasks."""
    task_id = task.get("id") or task.get("task_id", "")
    if not task_id:
        return False

    # SoC enforcement: daemons must NEVER claim tasks
    resolved_id = _AGENT_MAP.get(agent_name.lower(), agent_name)
    if resolved_id in _DAEMON_IDS or agent_name in _DAEMON_IDS:
        return False

    # Guard: reject bare agent names — must be a proper worker ID
    if not _is_valid_worker_id(agent_name):
        return False

    claimed = [False]

    def _claim(board):
        for d in board.get("dispatches", []):
            for t in d.get("tasks", []):
                t_id = t.get("id") or t.get("task_id", "")
                if t_id == task_id and t.get("status") == "open":
                    t["status"] = "active"
                    t["claimed_by"] = resolved_id
                    t["claimed_at"] = _now_iso()
                    claimed[0] = True
                    return board
        return board

    atomic_update_json(TASK_BOARD_FILE, _claim, default={"dispatches": []})
    return claimed[0]
