"""
Swarm Job System — System Job Definitions

System jobs defined in code (not YAML). These are provisioned automatically
and cannot be deleted by users. User jobs live in user-jobs.yaml.

Schedule format: standard 5-field cron (minute hour dom month dow).
Dependency format: "after:<job-id>" — runs after dependency completes (success or failure).
"""

from __future__ import annotations

import os
from pathlib import Path

from .models import Job, JobSafety


def _get_swarmai_root() -> str:
    """Resolve the swarmai source tree root for script job cwd.

    Works in both contexts:
    - Dev: __file__ is inside swarmai/backend/jobs/ → parents[2] works
    - Daemon binary: __file__ resolves to daemon/_internal/ → parents[2] is WRONG

    Resolution order:
    1. SWARMAI_SOURCE env var (explicit override, used by loops_health_check.py too)
    2. Path.home() / "Desktop/SwarmAI-Workspace/swarmai" (canonical dev location)
    3. __file__-based fallback (only correct in dev, kept for compatibility)
    """
    # 1. Env var (highest priority — works in all contexts)
    env_source = os.environ.get("SWARMAI_SOURCE")
    if env_source and Path(env_source).is_dir():
        return env_source

    # 2. Canonical locations (platform-aware)
    candidates = [
        Path.home() / "Desktop" / "SwarmAI-Workspace" / "swarmai",  # macOS dev
        Path.home() / "swarmai",                                      # Hive EC2
        Path("/opt/swarmai"),                                         # Hive alternate
    ]
    for candidate in candidates:
        if candidate.is_dir():
            return str(candidate)

    # 3. __file__-based (only correct in dev, but better than nothing)
    file_based = str(Path(__file__).resolve().parents[2])
    return file_based


# swarmai/ root — used as cwd for script jobs that need `python -m backend.jobs.*`
_SWARMAI_ROOT = _get_swarmai_root()

# All times in UTC
SYSTEM_JOBS: list[Job] = [
    # --- Signal Pipeline ---
    Job(
        id="signal-fetch",
        name="Fetch Signals",
        type="signal_fetch",
        schedule="0 2,8,14 * * *",   # 3x daily: ICT 10:00, 16:00, 22:00
        enabled=True,
        category="system",
        config={"max_age_hours": 48},
    ),
    Job(
        id="signal-digest",
        name="Digest Signals",
        type="signal_digest",
        schedule="after:signal-fetch",
        enabled=True,
        category="system",
        config={},
    ),

    # --- Self-Tune ---
    Job(
        id="self-tune",
        name="Self-Tune Feeds",
        type="self_tune",
        schedule="0 1 * * *",          # Daily ICT 09:00 (before first fetch at ICT 10:00)
        enabled=True,
        category="system",
        config={},
    ),

    # --- Maintenance (lightweight: prune caches, trim state, reset counters) ---
    # Monday 11:00 ICT (03:00 UTC) — start of work week, user is at desk.
    # If laptop was closed, cron_utils catch-up (48h window) retriggers on next boot.
    Job(
        id="weekly-maintenance",
        name="Weekly Maintenance",
        type="maintenance",
        schedule="0 3 * * 1",          # Monday 03:00 UTC = 11:00 ICT
        enabled=True,
        category="system",
        config={},
    ),

    # --- Memory Health (LLM-powered: stale entry pruning, gap detection) ---
    Job(
        id="memory-health",
        name="Memory Health Check",
        type="memory_health",
        schedule="15 3 * * 1",         # Monday 03:15 UTC = 11:15 ICT
        enabled=True,
        category="system",
        config={},
    ),

    # --- DDD Auto-Refresh (detect stale project docs, generate proposals) ---
    Job(
        id="ddd-refresh",
        name="DDD Auto-Refresh",
        type="ddd_refresh",
        schedule="30 3 * * 1",         # Monday 03:30 UTC = 11:30 ICT
        enabled=True,
        category="system",
        config={},
    ),

    # --- DDD Weekly Report (summarizes cultivation activity across all projects) ---
    # Runs after ddd-refresh (which proposes updates). This report surfaces
    # what was auto-applied, what needs escalation, and DDD health per project.
    # Output: Knowledge/Reports/YYYY-MM-DD-ddd-weekly.md
    Job(
        id="ddd-weekly-report",
        name="DDD Weekly Report",
        type="ddd_weekly_report",
        schedule="0 4 * * 1",           # Monday 04:00 UTC = 12:00 ICT (after ddd-refresh)
        enabled=True,
        category="system",
        config={"window_days": 7},
    ),

    # --- SwarmAI Monthly Report (comprehensive health + progress MBR) ---
    # Covers all 12 subsystems. Runs 1st of month after all weekly jobs have
    # populated their data for the prior month.
    Job(
        id="swarmai-monthly-report",
        name="SwarmAI Monthly Report",
        type="swarmai_monthly_report",
        schedule="0 5 1 * *",           # 1st of month, 05:00 UTC = 13:00 ICT
        enabled=True,
        category="system",
        config={},  # Uses previous month by default
    ),

    # --- Skill Proposer (reads health_findings.json, proposes skills for gaps) ---
    # Decoupled from memory-health: health_findings.json is populated by
    # ContextHealthHook (every session) AND memory-health (weekly LLM).
    # Skill proposer works fine with stale/partial data — no reason to block
    # on memory-health success.
    Job(
        id="skill-proposer",
        name="Skill Proposer",
        type="skill_proposer",
        schedule="45 3 * * 1",          # Monday 03:45 UTC = 11:45 ICT
        enabled=True,
        category="system",
        config={},
    ),

    # --- Signal Digest → Slack Notification ---
    # Fires after each digest, reads signal_digest.json, sends top items as Slack DM.
    # Requires ~/.swarm-ai/notify-channels.yaml with slack.enabled=true.
    # Handler pre-flight skips gracefully (status="skipped") when config missing
    # — won't trigger circuit breaker.
    Job(
        id="signal-notify-slack",
        name="Signal Digest → Slack",
        type="notify",
        schedule="after:signal-digest",
        enabled=True,
        category="system",
        config={
            "channel": "slack",
            "source": "signal_digest",  # read from signal_digest.json
            "max_items": 10,
        },
    ),

    # --- Weekly Rollup ---
    Job(
        id="weekly-rollup",
        name="Weekly Signal Rollup",
        type="signal_digest",
        schedule="0 2 * * 1",          # Monday ICT 10:00 — fresh weekly rollup
        enabled=True,
        category="system",
        config={"window_days": 7},
    ),

    # --- Todo Resolution (daily auto-resolve stale Radar Todos) ---
    # 3-layer resolution: pipeline completion check, git keyword match,
    # staleness cancellation.  Closes the "todos pile up" gap — creation
    # paths outnumber resolution paths 5:2.5, this job balances it.
    Job(
        id="todo-resolution",
        name="Todo Resolution",
        type="todo_resolution",
        schedule="0 4 * * *",             # Daily 04:00 UTC = 12:00 ICT
        enabled=True,
        category="system",
        config={"stale_days": 21, "working_stale_days": 14, "git_days": 7},
    ),

    # --- Evolution Cycle (standalone fallback) ---
    # Primary trigger is session-close hook (evolution_maintenance_hook.py),
    # but if the user's laptop is closed for days, sessions don't end and
    # the hook never fires. This scheduled job ensures the mine→score→optimize
    # pipeline runs at least once per week regardless of session activity.
    # Uses the same run_evolution_cycle() as the hook — idempotent via the
    # .evolution_last_run state file (7-day minimum interval).
    Job(
        id="evolution-cycle",
        name="Evolution Cycle",
        type="script",
        schedule="0 4 * * 4",          # Thursday 04:00 UTC = 12:00 ICT
        enabled=True,
        category="system",
        config={
            "command": "python -m backend.jobs.run_evolution",
            "cwd": _SWARMAI_ROOT,
        },
    ),

    # --- Loops Health (7-dimension self-maintenance scan) ---
    # Scans context files, DailyActivity, Knowledge/, Projects/, Evolution state,
    # git backup, and infrastructure health (31 checks). Auto-fixes safe mechanical
    # issues. Reports Found/Fixed/Pending with a health score (0-100).
    # Script uses Path.home()/.swarm-ai — works in daemon context without shell env.
    # NOTE: directory has hyphen (s_loops-health) so python -m doesn't work;
    # use direct script path relative to _SWARMAI_ROOT (cwd).
    Job(
        id="loops-health",
        name="Self-Loops Health Monitor",
        type="script",
        schedule="0 6 * * 1",          # Monday 06:00 UTC = 14:00 ICT
        enabled=True,
        category="system",
        config={
            "command": (
                "python backend/skills/s_loops-health/scripts/loops_health_check.py"
                " --auto-fix"
                " --output-dir ${HOME}/.swarm-ai/SwarmWS/Knowledge/JobResults"
                " --alert-threshold 70"
            ),
            "cwd": _SWARMAI_ROOT,
        },
        safety=JobSafety(max_budget_usd=0, timeout_seconds=300),
    ),

    # --- Code Intelligence Reindex (event-driven) ---
    # Triggered by git_commit events emitted from auto_commit_hook.
    # Runs incremental reindex on projects with code_intel.db.
    # Also triggered by code_intel_full_reindex for >50 stale files.
    Job(
        id="code-intel-reindex",
        name="Code Intelligence Reindex",
        type="script",
        schedule="on:git_commit",
        enabled=True,
        category="system",
        config={
            "command": "python -m backend.jobs.handlers.code_intel_reindex",
            "cwd": _SWARMAI_ROOT,
        },
        safety=JobSafety(max_budget_usd=0, timeout_seconds=60),
    ),

    # --- Code Intelligence Full Reindex (event-driven) ---
    # Triggered when context_health_hook detects >50 stale files.
    # Does a full rebuild rather than incremental.
    Job(
        id="code-intel-full-reindex",
        name="Code Intelligence Full Reindex",
        type="script",
        schedule="on:code_intel_full_reindex",
        enabled=True,
        category="system",
        config={
            "command": "python -m backend.jobs.handlers.code_intel_reindex --full",
            "cwd": _SWARMAI_ROOT,
        },
        safety=JobSafety(max_budget_usd=0, timeout_seconds=120),
    ),
]

SYSTEM_JOB_IDS: set[str] = {j.id for j in SYSTEM_JOBS}


def get_all_system_jobs() -> list[Job]:
    """Return a copy of all system job definitions."""
    return list(SYSTEM_JOBS)
