import { createContext, useContext, useState, useCallback, useMemo, ReactNode } from "react";
import type { SerializedTask } from "@/components/TaskRow";

// ---------------------------------------------------------------------------
// Types
// ---------------------------------------------------------------------------

export interface TaskCounts {
  complete: number;
  total: number;
}

export interface FlatTaskEntry {
  task: SerializedTask;
  phaseName: string;
  agentName: string | null;
  blockedBy: Array<{ name: string; status: string }>;
  dependents: Array<{ name: string; status: string }>;
}

export interface PhaseData {
  id: string;
  initiativeId: string;
  name: string;
  content: string | null;
  status: string;
  createdAt: string | Date;
  updatedAt: string | Date;
}

// ---------------------------------------------------------------------------
// Context
// ---------------------------------------------------------------------------

interface ExecutionContextValue {
  // Task selection
  selectedTaskId: string | null;
  setSelectedTaskId: (taskId: string | null) => void;

  // Task counts by phase
  taskCountsByPhase: Record<string, TaskCounts>;
  handleTaskCounts: (phaseId: string, counts: TaskCounts) => void;

  // Tasks by phase
  tasksByPhase: Record<string, FlatTaskEntry[]>;
  handleRegisterTasks: (phaseId: string, entries: FlatTaskEntry[]) => void;

  // Derived data
  allFlatTasks: FlatTaskEntry[];
  selectedEntry: FlatTaskEntry | null;
  tasksComplete: number;
  tasksTotal: number;
}

const ExecutionContext = createContext<ExecutionContextValue | null>(null);

// ---------------------------------------------------------------------------
// Provider
// ---------------------------------------------------------------------------

interface ExecutionProviderProps {
  children: ReactNode;
}

export function ExecutionProvider({ children }: ExecutionProviderProps) {
  const [selectedTaskId, setSelectedTaskId] = useState<string | null>(null);
  const [taskCountsByPhase, setTaskCountsByPhase] = useState<
    Record<string, TaskCounts>
  >({});
  const [tasksByPhase, setTasksByPhase] = useState<
    Record<string, FlatTaskEntry[]>
  >({});

  const handleTaskCounts = useCallback(
    (phaseId: string, counts: TaskCounts) => {
      setTaskCountsByPhase((prev) => {
        if (
          prev[phaseId]?.complete === counts.complete &&
          prev[phaseId]?.total === counts.total
        ) {
          return prev;
        }
        return { ...prev, [phaseId]: counts };
      });
    },
    [],
  );

  const handleRegisterTasks = useCallback(
    (phaseId: string, entries: FlatTaskEntry[]) => {
      setTasksByPhase((prev) => {
        if (prev[phaseId] === entries) return prev;
        return { ...prev, [phaseId]: entries };
      });
    },
    [],
  );

  const allFlatTasks = useMemo(
    () => Object.values(tasksByPhase).flat(),
    [tasksByPhase]
  );

  const selectedEntry = useMemo(
    () => selectedTaskId
      ? allFlatTasks.find((e) => e.task.id === selectedTaskId) ?? null
      : null,
    [selectedTaskId, allFlatTasks]
  );

  const { tasksComplete, tasksTotal } = useMemo(() => {
    const allTaskCounts = Object.values(taskCountsByPhase);
    return {
      tasksComplete: allTaskCounts.reduce((s, c) => s + c.complete, 0),
      tasksTotal: allTaskCounts.reduce((s, c) => s + c.total, 0),
    };
  }, [taskCountsByPhase]);

  const value: ExecutionContextValue = {
    selectedTaskId,
    setSelectedTaskId,
    taskCountsByPhase,
    handleTaskCounts,
    tasksByPhase,
    handleRegisterTasks,
    allFlatTasks,
    selectedEntry,
    tasksComplete,
    tasksTotal,
  };

  return (
    <ExecutionContext.Provider value={value}>
      {children}
    </ExecutionContext.Provider>
  );
}

// ---------------------------------------------------------------------------
// Hook
// ---------------------------------------------------------------------------

export function useExecutionContext() {
  const context = useContext(ExecutionContext);
  if (!context) {
    throw new Error("useExecutionContext must be used within ExecutionProvider");
  }
  return context;
}