/**
 * Agent scheduler — checks locally-stored agent configs on a periodic
 * interval and triggers BE-orchestrated runs when they are due.
 *
 * Follows the same pattern as the daily brief scheduler in orchestrator.ts:
 * a single `setInterval` tick that checks all enabled agents.
 */

import { getLocalAgents, saveLocalAgent, getDeviceId } from '../store';
import { getBackendClient } from '../api/backend-client';
import { getDb } from '../db';
import { agentRuns } from '../db/schema';

// ---------------------------------------------------------------------------
// Constants
// ---------------------------------------------------------------------------

/** How often the scheduler checks for due agents (ms). */
const TICK_INTERVAL_MS = 60_000; // 60 seconds

/**
 * Cron expression → minimum interval in ms.
 * We use a simple mapping for the supported presets; unknown cron values
 * are treated as manual-only.
 */
const CRON_INTERVAL_MS: Record<string, number> = {
  '*/15 * * * *': 15 * 60 * 1000,
  '0 * * * *': 60 * 60 * 1000,
  '0 */6 * * *': 6 * 60 * 60 * 1000,
  '0 0 * * *': 24 * 60 * 60 * 1000,
};

// ---------------------------------------------------------------------------
// State
// ---------------------------------------------------------------------------

let schedulerTimer: ReturnType<typeof setInterval> | null = null;

// ---------------------------------------------------------------------------
// Public API
// ---------------------------------------------------------------------------

export function startAgentScheduler(): void {
  if (schedulerTimer) return;

  schedulerTimer = setInterval(() => {
    void tickAgentScheduler();
  }, TICK_INTERVAL_MS);

  // Run once immediately on start
  void tickAgentScheduler();
}

export function stopAgentScheduler(): void {
  if (schedulerTimer) {
    clearInterval(schedulerTimer);
    schedulerTimer = null;
  }
}

// ---------------------------------------------------------------------------
// Tick
// ---------------------------------------------------------------------------

async function tickAgentScheduler(): Promise<void> {
  const agents = getLocalAgents();
  const now = Date.now();

  for (const agent of agents) {
    if (!agent.enabled) continue;

    // Manual-only agents don't auto-trigger
    const intervalMs = CRON_INTERVAL_MS[agent.scheduleCron];
    if (!intervalMs) continue;

    // Check if enough time has passed since lastRunAt
    if (agent.lastRunAt && now - agent.lastRunAt < intervalMs) continue;

    try {
      const activeAgents = agents.length;
      console.log(
        `[AgentScheduler] Triggering agent "${agent.name}" (id=${agent.id}) with lastRunAt=${agent.lastRunAt} (${agent.lastRunAt ? new Date(agent.lastRunAt).toISOString() : 'null'})`,
      );
      const response = await getBackendClient().proxyPost<{ id: string }>(
        '/api/v1/agents/trigger',
        {
          directory: agent.directory,
          deviceId: getDeviceId(),
          agentId: agent.id,
          whatToExtract: agent.dataTypes,
          batchInterval: agent.scheduleCron,
          agentConfig: agent.agentConfig ?? undefined,
          activeAgents,
          lastRunAt: agent.lastRunAt ?? undefined,
        },
      );

      // Create the run row immediately so it appears in history even if
      // the agent finds nothing to create/update.
      if (response?.id) {
        try {
          await getDb().insert(agentRuns).values({
            id: response.id,
            agentId: agent.id,
            status: 'running',
            startedAt: now,
          }).onConflictDoNothing();
        } catch { /* ignore — row may already exist */ }
      }

      // Mark the run time so we don't re-trigger until the next interval
      saveLocalAgent({ ...agent, lastRunAt: now });
      console.log(`[AgentScheduler] Triggered agent "${agent.name}" (id=${agent.id}).`);
    } catch (err) {
      const msg = err instanceof Error ? err.message : String(err);
      console.warn(`[AgentScheduler] Failed to trigger agent "${agent.name}": ${msg}`);
    }
  }
}
