import {
  createContext,
  useCallback,
  useContext,
  useMemo,
  useState,
  type ReactNode,
} from "react";

import type { InstanceEventPayload } from "../types/events";

export type OperationType =
  | "create"
  | "start"
  | "stop"
  | "restart"
  | "delete"
  | "recreate-tunnel";

export type OperationStatus = "pending" | "active" | "success" | "error";

export interface Operation {
  id: string;
  type: OperationType;
  instanceId: string;
  displayName: string;
  status: OperationStatus;
  message: string;
  step: number;
  createdAt: number;
}

export interface SessionOperationsContextType {
  operations: Operation[];
  startOperation: (
    type: OperationType,
    instanceId: string,
    displayName: string,
  ) => string;
  updateOperationFromEvent: (event: InstanceEventPayload) => void;
  completeOperation: (
    instanceId: string,
    type: OperationType,
    outcome: "success" | "error",
    message?: string,
  ) => void;
  dismissOperation: (id: string) => void;
}

const SessionOperationsContext =
  createContext<SessionOperationsContextType | undefined>(undefined);

let operationIdCounter = 0;

function actionLabel(type: OperationType): string {
  switch (type) {
    case "create":
      return "Creating";
    case "start":
      return "Starting";
    case "stop":
      return "Stopping";
    case "restart":
      return "Restarting";
    case "delete":
      return "Deleting";
    case "recreate-tunnel":
      return "Recreating tunnel";
    default:
      return "Working";
  }
}

function messageForEvent(
  type: OperationType,
  event: InstanceEventPayload,
): string {
  if (event.message) return event.message;

  switch (event.event) {
    case "instance.created":
      return "Created";
    case "instance.started":
      return "Starting container";
    case "instance.restarted":
      return "Restarting container";
    case "instance.stopped":
      return "Stopped";
    case "instance.deleted":
      return "Deleted";
    case "instance.health_changed":
      if (event.status === "running") return "Running";
      if (event.status === "unhealthy") return "Unhealthy";
      return `Status: ${event.status ?? event.event}`;
    case "instance.error":
      return event.message ?? "Error";
    default:
      return event.message ?? actionLabel(type);
  }
}

function stepForEvent(event: InstanceEventPayload): number {
  switch (event.event) {
    case "instance.created":
      return 1;
    case "instance.started":
    case "instance.restarted":
      return 2;
    case "instance.health_changed":
      if (event.status === "running") return 4;
      if (event.status === "unhealthy") return 4;
      return 3;
    case "instance.error":
      return 4;
    case "instance.stopped":
      return 4;
    case "instance.deleted":
      return 4;
    default:
      return 0;
  }
}

export const SessionOperationsProvider = ({
  children,
}: {
  children: ReactNode;
}) => {
  const [operations, setOperations] = useState<Operation[]>([]);

  const startOperation = useCallback(
    (type: OperationType, instanceId: string, displayName: string): string => {
      const id = `op-${++operationIdCounter}`;
      const operation: Operation = {
        id,
        type,
        instanceId,
        displayName,
        status: "pending",
        message: actionLabel(type),
        step: 0,
        createdAt: Date.now(),
      };
      setOperations((prev) => [operation, ...prev].slice(0, 20));
      return id;
    },
    [],
  );

  const updateOperationFromEvent = useCallback(
    (event: InstanceEventPayload) => {
      setOperations((prev) => {
        const matches = prev.filter(
          (op) => op.instanceId === event.instance_id && op.status !== "success",
        );
        if (matches.length === 0) return prev;

        const updated = new Map<string, Operation>();
        for (const op of prev) updated.set(op.id, op);

        for (const op of matches) {
          const nextStep = stepForEvent(event);
          const message = messageForEvent(op.type, event);
          let nextStatus: OperationStatus = op.status;

          if (event.event === "instance.error") {
            nextStatus = "error";
          } else if (
            event.event === "instance.health_changed" &&
            event.status === "running"
          ) {
            nextStatus = "success";
          } else if (event.event === "instance.deleted") {
            nextStatus = "success";
          } else if (event.event === "instance.stopped") {
            nextStatus = "success";
          } else if (nextStatus === "pending") {
            nextStatus = "active";
          }

          updated.set(op.id, {
            ...op,
            status: nextStatus,
            message,
            step: Math.max(op.step, nextStep),
          });
        }

        return Array.from(updated.values());
      });
    },
    [],
  );

  const completeOperation = useCallback(
    (
      instanceId: string,
      type: OperationType,
      outcome: "success" | "error",
      message?: string,
    ) => {
      setOperations((prev) => {
        const match = prev.find(
          (op) => op.instanceId === instanceId && op.type === type,
        );
        if (!match) return prev;
        return prev.map((op) =>
          op.id === match.id
            ? {
                ...op,
                status: outcome,
                message:
                  message ?? (outcome === "success" ? "Done" : "Failed"),
                step: 4,
              }
            : op,
        );
      });
    },
    [],
  );

  const dismissOperation = useCallback((id: string) => {
    setOperations((prev) => prev.filter((op) => op.id !== id));
  }, []);

  const value = useMemo(
    () => ({
      operations,
      startOperation,
      updateOperationFromEvent,
      completeOperation,
      dismissOperation,
    }),
    [
      operations,
      startOperation,
      updateOperationFromEvent,
      completeOperation,
      dismissOperation,
    ],
  );

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

export const useSessionOperations = (): SessionOperationsContextType => {
  const context = useContext(SessionOperationsContext);
  if (context === undefined) {
    throw new Error(
      "useSessionOperations must be used within a SessionOperationsProvider",
    );
  }
  return context;
};
