// ABOUTME: Agent tasks store for managing cloud agent task state.
// ABOUTME: Provides reactive task list, active task tracking, and SSE streaming.

import { createStore } from "solid-js/store";
import {
  type AgentTask,
  type AgentTaskStatus,
  cancelAgentTask,
  getAgentTask,
  isTerminalStatus,
  listAgentTasks,
  resumeAgentTask,
  runAgentCloud,
  streamTask,
} from "@/services/agent-tasks";

/** A streaming event received via SSE for display in the live log. */
export interface StreamEvent {
  eventType: string;
  data: Record<string, unknown>;
  receivedAt: number;
}

interface AgentTasksState {
  tasks: AgentTask[];
  activeTaskId: string | null;
  /** Per-task accumulated streaming events, keyed by task ID. */
  taskEvents: Record<string, StreamEvent[]>;
  isLoading: boolean;
  error: string | null;
  total: number;
  offset: number;
  limit: number;
}

const initialState: AgentTasksState = {
  tasks: [],
  activeTaskId: null,
  taskEvents: {},
  isLoading: false,
  error: null,
  total: 0,
  offset: 0,
  limit: 20,
};

const [state, setState] = createStore<AgentTasksState>(initialState);

// Active stream handle for cleanup.
let activeStream: { close: () => void } | null = null;

/**
 * Load tasks for an organization.
 */
async function loadTasks(orgId: string): Promise<void> {
  setState("isLoading", true);
  setState("error", null);
  try {
    const tasks = await listAgentTasks(orgId, state.limit, state.offset);
    setState("tasks", tasks);
    setState("total", tasks.length);
  } catch (err) {
    setState("error", err instanceof Error ? err.message : String(err));
  } finally {
    setState("isLoading", false);
  }
}

/**
 * Run an agent and track the resulting task.
 */
async function runAgent(
  orgId: string,
  publisherSlug: string,
  message: unknown,
): Promise<AgentTask> {
  setState("error", null);
  const task = await runAgentCloud(publisherSlug, message);
  // Prepend to task list
  setState("tasks", (prev) => [task, ...prev]);
  setState("total", (prev) => prev + 1);
  // Start streaming updates
  followTask(orgId, task.id);
  return task;
}

/**
 * Start streaming updates for a task.
 */
function followTask(orgId: string, taskId: string): void {
  // Close any existing stream
  stopFollowing();

  setState("activeTaskId", taskId);

  // Keep prior event history if this task was already followed.
  if (!state.taskEvents[taskId]) {
    setState("taskEvents", taskId, []);
  }

  activeStream = streamTask(orgId, taskId, {
    onEvent: (eventType, data) => {
      if (state.activeTaskId !== taskId) return;

      // Accumulate event for live log display.
      setState("taskEvents", taskId, (prev) => [
        ...(prev ?? []),
        { eventType, data, receivedAt: Date.now() },
      ]);

      // Update the task in the list if we get status info.
      if (typeof data.status === "string") {
        updateTaskInList(taskId, {
          status: data.status as AgentTaskStatus,
        });
      }
    },
    onComplete: (taskData) => {
      if (state.activeTaskId !== taskId) return;

      const updates: Partial<AgentTask> = {
        status: (taskData.status as AgentTaskStatus) ?? "completed",
      };

      if (taskData.output !== undefined) {
        updates.output = taskData.output as Record<string, unknown>;
      }
      if (taskData.error_message !== undefined) {
        updates.error_message = taskData.error_message as string;
      }
      if (typeof taskData.cost_total_atomic === "number") {
        updates.cost_total_atomic = taskData.cost_total_atomic;
      }

      updateTaskInList(taskId, updates);
      activeStream = null;
    },
    onError: (error) => {
      if (state.activeTaskId !== taskId) return;

      console.error("[AgentTasks] Stream error:", error);
      activeStream = null;
      // Fallback: poll the task once.
      void refreshTask(orgId, taskId);
    },
  });
}

/**
 * Stop following the current task stream.
 */
function stopFollowing(): void {
  if (activeStream) {
    activeStream.close();
    activeStream = null;
  }
}

/**
 * Refresh a single task from the API.
 */
async function refreshTask(orgId: string, taskId: string): Promise<void> {
  try {
    const task = await getAgentTask(orgId, taskId);
    updateTaskInList(taskId, task);
  } catch (err) {
    console.error("[AgentTasks] Failed to refresh task:", err);
  }
}

/**
 * Cancel a task.
 */
async function cancelTask(orgId: string, taskId: string): Promise<void> {
  try {
    const task = await cancelAgentTask(orgId, taskId);
    updateTaskInList(taskId, task);
    if (state.activeTaskId === taskId && isTerminalStatus(task.status)) {
      stopFollowing();
    }
  } catch (err) {
    setState("error", err instanceof Error ? err.message : String(err));
  }
}

/**
 * Resume a task that is waiting for additional user input.
 */
async function resumeTask(
  orgId: string,
  taskId: string,
  input: unknown,
): Promise<void> {
  try {
    const task = await resumeAgentTask(orgId, taskId, { input });
    updateTaskInList(taskId, task);
    followTask(orgId, taskId);
  } catch (err) {
    setState("error", err instanceof Error ? err.message : String(err));
    throw err;
  }
}

/**
 * Update a task in the list by ID.
 */
function updateTaskInList(taskId: string, updates: Partial<AgentTask>): void {
  setState(
    "tasks",
    (task) => task.id === taskId,
    (prev) => ({ ...prev, ...updates }),
  );
}

/**
 * Get the currently active task.
 */
function getActiveTask(): AgentTask | undefined {
  if (!state.activeTaskId) return undefined;
  return state.tasks.find((t) => t.id === state.activeTaskId);
}

/**
 * Get accumulated streaming events for a task.
 */
function getEventsForTask(taskId: string): StreamEvent[] {
  return state.taskEvents[taskId] ?? [];
}

/**
 * Reset store state (e.g., on logout).
 */
function resetAgentTasksState(): void {
  stopFollowing();
  setState(initialState);
}

export {
  cancelTask,
  followTask,
  getActiveTask,
  getEventsForTask,
  isTerminalStatus,
  loadTasks,
  refreshTask,
  resetAgentTasksState,
  resumeTask,
  runAgent,
  state as agentTasksState,
  stopFollowing,
};
