/**
 * Task Progress Widget - shows orchestrator agent task progress
 *
 * Displays live progress of agent tasks being executed by the orchestrator,
 * including status, elapsed time, and work item details.
 */

import { useState, useEffect, useCallback } from "react";
import { Activity, CheckCircle, XCircle, Clock, Loader2, Pause, Play } from "lucide-react";
import type { WidgetProps } from "@mosaic/shared";

interface AgentTask {
  agentId: string;
  taskId: string;
  status: string;
  agentType: string;
  spawnedAt: string;
  completedAt?: string;
  error?: string;
}

interface QueueStats {
  pending: number;
  active: number;
  completed: number;
  failed: number;
  delayed: number;
}

interface RecentOrchestratorEvent {
  type: string;
  timestamp: string;
  taskId?: string;
  agentId?: string;
}

function getElapsedTime(spawnedAt: string, completedAt?: string): string {
  const start = new Date(spawnedAt).getTime();
  const end = completedAt ? new Date(completedAt).getTime() : Date.now();
  const diffMs = end - start;

  if (diffMs < 60000) return `${String(Math.floor(diffMs / 1000))}s`;
  if (diffMs < 3600000)
    return `${String(Math.floor(diffMs / 60000))}m ${String(Math.floor((diffMs % 60000) / 1000))}s`;
  return `${String(Math.floor(diffMs / 3600000))}h ${String(Math.floor((diffMs % 3600000) / 60000))}m`;
}

function getStatusIcon(status: string): React.JSX.Element {
  switch (status) {
    case "running":
      return <Activity className="w-4 h-4 text-blue-500 animate-pulse" />;
    case "spawning":
      return <Loader2 className="w-4 h-4 text-yellow-500 animate-spin" />;
    case "completed":
      return <CheckCircle className="w-4 h-4 text-green-500" />;
    case "failed":
    case "killed":
      return <XCircle className="w-4 h-4 text-amber-500" />;
    default:
      return <Clock className="w-4 h-4 text-gray-400" />;
  }
}

function getStatusLabel(status: string): string {
  switch (status) {
    case "spawning":
      return "Starting";
    case "running":
      return "In progress";
    case "completed":
      return "Done";
    case "failed":
      return "Stopped";
    case "killed":
      return "Terminated";
    default:
      return status;
  }
}

function getStatusColor(status: string): string {
  switch (status) {
    case "running":
      return "bg-blue-50 border-blue-200 dark:bg-blue-950 dark:border-blue-800";
    case "spawning":
      return "bg-yellow-50 border-yellow-200 dark:bg-yellow-950 dark:border-yellow-800";
    case "completed":
      return "bg-green-50 border-green-200 dark:bg-green-950 dark:border-green-800";
    case "failed":
    case "killed":
      return "bg-amber-50 border-amber-200 dark:bg-amber-950 dark:border-amber-800";
    default:
      return "bg-gray-50 border-gray-200 dark:bg-gray-900 dark:border-gray-700";
  }
}

function getAgentTypeLabel(agentType: string): string {
  switch (agentType) {
    case "worker":
      return "Worker";
    case "reviewer":
      return "Reviewer";
    case "tester":
      return "Tester";
    default:
      return agentType;
  }
}

export function TaskProgressWidget({ id: _id, config: _config }: WidgetProps): React.JSX.Element {
  const [tasks, setTasks] = useState<AgentTask[]>([]);
  const [queueStats, setQueueStats] = useState<QueueStats | null>(null);
  const [recentEvents, setRecentEvents] = useState<RecentOrchestratorEvent[]>([]);
  const [isLoading, setIsLoading] = useState(true);
  const [error, setError] = useState<string | null>(null);
  const [isQueuePaused, setIsQueuePaused] = useState(false);
  const [isActionPending, setIsActionPending] = useState(false);

  const fetchTasks = useCallback(async (): Promise<void> => {
    try {
      const res = await fetch("/api/orchestrator/agents");
      if (!res.ok) throw new Error(`HTTP ${String(res.status)}`);
      const data = (await res.json()) as AgentTask[];
      setTasks(data);
      setError(null);
      setIsLoading(false);
    } catch {
      setError("Unable to reach orchestrator");
      setIsLoading(false);
    }
  }, []);

  const fetchQueueStats = useCallback(async (): Promise<void> => {
    try {
      const res = await fetch("/api/orchestrator/queue/stats");
      if (!res.ok) throw new Error(`HTTP ${String(res.status)}`);
      const data = (await res.json()) as QueueStats;
      setQueueStats(data);
      // Heuristic: active=0 with pending>0 for sustained windows usually means paused.
      setIsQueuePaused(data.active === 0 && data.pending > 0);
    } catch {
      // Keep widget functional even if queue controls are temporarily unavailable.
    }
  }, []);

  const fetchRecentEvents = useCallback(async (): Promise<void> => {
    try {
      const res = await fetch("/api/orchestrator/events/recent?limit=5");
      if (!res.ok) throw new Error(`HTTP ${String(res.status)}`);
      const payload = (await res.json()) as unknown;
      const events =
        payload &&
        typeof payload === "object" &&
        "events" in payload &&
        Array.isArray(payload.events)
          ? (payload.events as RecentOrchestratorEvent[])
          : [];
      setRecentEvents(events);
    } catch {
      // Optional enhancement path; do not fail widget if recent-events endpoint is unavailable.
    }
  }, []);

  const setQueueState = useCallback(
    async (action: "pause" | "resume"): Promise<void> => {
      setIsActionPending(true);
      try {
        const res = await fetch(`/api/orchestrator/queue/${action}`, {
          method: "POST",
        });
        if (!res.ok) throw new Error(`HTTP ${String(res.status)}`);
        setIsQueuePaused(action === "pause");
        await fetchQueueStats();
      } catch {
        setError("Unable to control queue state");
      } finally {
        setIsActionPending(false);
      }
    },
    [fetchQueueStats]
  );

  useEffect(() => {
    void fetchTasks();
    void fetchQueueStats();
    void fetchRecentEvents();

    const interval = setInterval(() => {
      void fetchTasks();
      void fetchQueueStats();
      void fetchRecentEvents();
    }, 15000);

    const eventSource =
      typeof EventSource !== "undefined" ? new EventSource("/api/orchestrator/events") : null;
    if (eventSource) {
      eventSource.onmessage = (): void => {
        void fetchTasks();
        void fetchQueueStats();
        void fetchRecentEvents();
      };
      eventSource.onerror = (): void => {
        // Polling remains the resilience path.
      };
    }

    return (): void => {
      clearInterval(interval);
      eventSource?.close();
    };
  }, [fetchTasks, fetchQueueStats, fetchRecentEvents]);

  const latestEvent = recentEvents.length > 0 ? recentEvents[recentEvents.length - 1] : null;

  const stats = {
    total: tasks.length,
    active: tasks.filter((t) => t.status === "running" || t.status === "spawning").length,
    completed: tasks.filter((t) => t.status === "completed").length,
    failed: tasks.filter((t) => t.status === "failed" || t.status === "killed").length,
  };

  if (isLoading) {
    return (
      <div className="flex items-center justify-center h-full">
        <Loader2 className="w-5 h-5 text-gray-400 animate-spin" />
        <span className="ml-2 text-gray-500 text-sm">Loading task progress...</span>
      </div>
    );
  }

  if (error) {
    return (
      <div className="flex flex-col items-center justify-center h-full text-center">
        <XCircle className="w-6 h-6 text-gray-400 mb-2" />
        <span className="text-gray-500 text-sm">{error}</span>
        <span className="text-gray-400 text-xs mt-1">Retrying automatically</span>
      </div>
    );
  }

  return (
    <div className="flex flex-col h-full space-y-3">
      <div className="flex items-center justify-between">
        <div className="text-xs text-gray-500 dark:text-gray-400">
          Queue: {isQueuePaused ? "Paused" : "Running"}
        </div>
        <button
          type="button"
          onClick={(): void => {
            void setQueueState(isQueuePaused ? "resume" : "pause");
          }}
          disabled={isActionPending}
          className="inline-flex items-center gap-1 rounded border border-gray-300 dark:border-gray-700 px-2 py-1 text-xs hover:bg-gray-100 dark:hover:bg-gray-800 disabled:opacity-50"
        >
          {isQueuePaused ? <Play className="w-3 h-3" /> : <Pause className="w-3 h-3" />}
          {isQueuePaused ? "Resume" : "Pause"}
        </button>
      </div>

      {latestEvent && (
        <div className="rounded bg-gray-50 dark:bg-gray-800 px-2 py-1 text-xs text-gray-600 dark:text-gray-300">
          Latest: {latestEvent.type}
          {latestEvent.taskId ? ` · ${latestEvent.taskId}` : ""}
        </div>
      )}

      {/* Summary stats */}
      <div className="grid grid-cols-4 gap-1 text-center text-xs">
        <div className="bg-gray-50 dark:bg-gray-800 rounded p-2">
          <div className="text-lg font-bold text-gray-900 dark:text-gray-100">{stats.total}</div>
          <div className="text-gray-500">Total</div>
        </div>
        <div className="bg-blue-50 dark:bg-blue-950 rounded p-2">
          <div className="text-lg font-bold text-blue-600 dark:text-blue-400">{stats.active}</div>
          <div className="text-blue-500">Active</div>
        </div>
        <div className="bg-green-50 dark:bg-green-950 rounded p-2">
          <div className="text-lg font-bold text-green-600 dark:text-green-400">
            {stats.completed}
          </div>
          <div className="text-green-500">Done</div>
        </div>
        <div className="bg-amber-50 dark:bg-amber-950 rounded p-2">
          <div className="text-lg font-bold text-amber-600 dark:text-amber-400">{stats.failed}</div>
          <div className="text-amber-500">Stopped</div>
        </div>
      </div>

      {queueStats && (
        <div className="grid grid-cols-3 gap-1 text-center text-xs">
          <div className="bg-gray-50 dark:bg-gray-800 rounded p-1">
            <div className="font-semibold text-gray-700 dark:text-gray-200">
              {queueStats.pending}
            </div>
            <div className="text-gray-500">Queued</div>
          </div>
          <div className="bg-gray-50 dark:bg-gray-800 rounded p-1">
            <div className="font-semibold text-gray-700 dark:text-gray-200">
              {queueStats.active}
            </div>
            <div className="text-gray-500">Workers</div>
          </div>
          <div className="bg-gray-50 dark:bg-gray-800 rounded p-1">
            <div className="font-semibold text-gray-700 dark:text-gray-200">
              {queueStats.failed}
            </div>
            <div className="text-gray-500">Failed</div>
          </div>
        </div>
      )}

      {/* Task list */}
      <div className="flex-1 overflow-auto space-y-2">
        {tasks.length === 0 ? (
          <div className="text-center text-gray-500 text-sm py-4">No agent tasks in progress</div>
        ) : (
          [...tasks]
            .sort((a, b) => {
              // Active tasks first, then by spawn time
              const statusOrder: Record<string, number> = {
                running: 0,
                spawning: 1,
                failed: 2,
                killed: 3,
                completed: 4,
              };
              const orderA = statusOrder[a.status] ?? 5;
              const orderB = statusOrder[b.status] ?? 5;
              if (orderA !== orderB) return orderA - orderB;
              return new Date(b.spawnedAt).getTime() - new Date(a.spawnedAt).getTime();
            })
            .slice(0, 10)
            .map((task) => (
              <div
                key={task.agentId}
                className={`p-2 rounded-lg border ${getStatusColor(task.status)}`}
              >
                <div className="flex items-center justify-between mb-1">
                  <div className="flex items-center gap-2">
                    {getStatusIcon(task.status)}
                    <span className="text-sm font-medium text-gray-900 dark:text-gray-100 truncate">
                      {task.taskId}
                    </span>
                  </div>
                  <span className="text-xs px-1.5 py-0.5 rounded bg-gray-200 dark:bg-gray-700 text-gray-600 dark:text-gray-300">
                    {getAgentTypeLabel(task.agentType)}
                  </span>
                </div>
                <div className="flex items-center justify-between text-xs text-gray-500 dark:text-gray-400">
                  <span>{getStatusLabel(task.status)}</span>
                  <span>{getElapsedTime(task.spawnedAt, task.completedAt)}</span>
                </div>
                {task.error && (
                  <div className="text-xs text-amber-600 dark:text-amber-400 mt-1 truncate">
                    {task.error}
                  </div>
                )}
              </div>
            ))
        )}
      </div>
    </div>
  );
}
