import { useEffect, useState } from "react";
import type { Dispatch, SetStateAction } from "react";
import {
  createGraphPreset,
  deleteGraphPreset,
  fetchGraphPresets,
  updateGraphPreset,
  type APIGraphCreationContext,
  type APIGraphPreset,
} from "../../services/medical";
import type { GraphDataset } from "../../types/medical";
import { createDatasetInstanceId } from "./graphHelpers.ts";

type GraphItemLike = {
  id: string;
  datasets: GraphDataset[];
  normalizeByReference?: boolean;
  creation: APIGraphCreationContext;
};

type GraphConfigSnapshot = {
  exportedAt: string;
  graphCount: number;
  graphs: APIGraphPreset["graphs"];
};

type UseGraphPresetsParams<TGraph extends GraphItemLike> = {
  userId?: string;
  graphs: TGraph[];
  setGraphs: Dispatch<SetStateAction<TGraph[]>>;
  availableDatasets: GraphDataset[];
};

export function useGraphPresets<TGraph extends GraphItemLike>({
  userId,
  graphs,
  setGraphs,
  availableDatasets,
}: UseGraphPresetsParams<TGraph>) {
  const [storedPresets, setStoredPresets] = useState<APIGraphPreset[]>([]);
  const [selectedPresetId, setSelectedPresetId] = useState<string>("");
  const [isLoadingPresets, setIsLoadingPresets] = useState(false);
  const [presetActionError, setPresetActionError] = useState<string | null>(null);
  const [isSavingPreset, setIsSavingPreset] = useState(false);
  const [isPresetModalOpen, setIsPresetModalOpen] = useState(false);
  const [presetModalMode, setPresetModalMode] = useState<"create" | "rename" | "delete" | null>(null);
  const [presetNameDraft, setPresetNameDraft] = useState("");

  const buildGraphConfigSnapshot = (): GraphConfigSnapshot => ({
    exportedAt: new Date().toISOString(),
    graphCount: graphs.length,
    graphs: graphs.map((graph) => ({
      id: graph.id,
      normalizeByReference: graph.normalizeByReference ?? false,
      creation: graph.creation,
      datasets: graph.datasets.map((dataset) => {
        const origin = graph.creation.datasetOrigins?.[dataset.id];
        return {
          id: dataset.id,
          name: dataset.name,
          type: dataset.type,
          unit: dataset.unit,
          color: dataset.color,
          lineStyle: dataset.lineStyle,
          pointStyle: dataset.pointStyle,
          dateFilterMode: origin?.dateFilterMode || graph.creation.dateFilterMode || "preset",
          selectedPreset: origin?.selectedPreset ?? graph.creation.selectedPreset,
          appliedDateRange: origin?.appliedDateRange ?? graph.creation.appliedDateRange ?? {},
        };
      }),
    })),
  });

  const loadStoredPresets = async () => {
    if (!userId) return;

    setIsLoadingPresets(true);
    setPresetActionError(null);

    try {
      const presets = await fetchGraphPresets(userId);
      setStoredPresets(presets);
    } catch {
      setPresetActionError("Unable to load saved presets");
    } finally {
      setIsLoadingPresets(false);
    }
  };

  const createPresetInBackend = async () => {
    if (!userId) return;
    if (graphs.length === 0) {
      setPresetActionError("Add at least one graph before creating a preset");
      return;
    }

    setPresetModalMode("create");
    setPresetNameDraft(`Plot preset ${new Date().toLocaleString()}`);
    setPresetActionError(null);
    setIsPresetModalOpen(true);
  };

  const saveSelectedPreset = async () => {
    if (!selectedPresetId) {
      setPresetActionError("Select a preset to save");
      return;
    }

    if (graphs.length === 0) {
      setPresetActionError("Add at least one graph before saving a preset");
      return;
    }

    setIsSavingPreset(true);
    setPresetActionError(null);

    try {
      const snapshot = buildGraphConfigSnapshot();
      await updateGraphPreset(selectedPresetId, {
        exportedAt: snapshot.exportedAt,
        graphCount: snapshot.graphCount,
        graphs: snapshot.graphs,
      });
      await loadStoredPresets();
    } catch {
      setPresetActionError("Unable to save preset");
    } finally {
      setIsSavingPreset(false);
    }
  };

  const renameSelectedPreset = async () => {
    const preset = storedPresets.find((presetItem) => presetItem._id === selectedPresetId);
    if (!preset) {
      setPresetActionError("Select a preset to rename");
      return;
    }

    setPresetModalMode("rename");
    setPresetNameDraft(preset.name);
    setPresetActionError(null);
    setIsPresetModalOpen(true);
  };

  const requestDeleteSelectedPreset = () => {
    const preset = storedPresets.find((presetItem) => presetItem._id === selectedPresetId);
    if (!preset) {
      setPresetActionError("Select a preset to delete");
      return;
    }

    setPresetModalMode("delete");
    setPresetNameDraft(preset.name);
    setPresetActionError(null);
    setIsPresetModalOpen(true);
  };

  const submitPresetModal = async () => {
    if (!presetModalMode) return;

    const name = presetNameDraft.trim();
    if (presetModalMode !== "delete" && !name) {
      setPresetActionError("Preset name cannot be empty");
      return;
    }

    setIsSavingPreset(true);
    setPresetActionError(null);

    try {
      if (presetModalMode === "create") {
        if (!userId) return;

        const snapshot = buildGraphConfigSnapshot();
        const created = await createGraphPreset({
          patientId: userId,
          name,
          exportedAt: snapshot.exportedAt,
          graphCount: snapshot.graphCount,
          graphs: snapshot.graphs,
        });
        setSelectedPresetId(created._id);
      }

      if (presetModalMode === "rename") {
        if (!selectedPresetId) {
          setPresetActionError("Select a preset to rename");
          return;
        }

        await updateGraphPreset(selectedPresetId, { name });
      }

      if (presetModalMode === "delete") {
        if (!selectedPresetId) {
          setPresetActionError("Select a preset to delete");
          return;
        }

        await deleteGraphPreset(selectedPresetId);
        setSelectedPresetId("");
      }

      await loadStoredPresets();
      setIsPresetModalOpen(false);
      setPresetModalMode(null);
    } catch {
      setPresetActionError(
        presetModalMode === "create"
          ? "Unable to create preset"
          : presetModalMode === "rename"
            ? "Unable to rename preset"
            : "Unable to delete preset"
      );
    } finally {
      setIsSavingPreset(false);
    }
  };

  const closePresetModal = () => {
    if (isSavingPreset) return;
    setIsPresetModalOpen(false);
    setPresetModalMode(null);
  };

  const loadSelectedPreset = () => {
    const preset = storedPresets.find((presetItem) => presetItem._id === selectedPresetId);
    if (!preset) {
      setPresetActionError("Select a preset to load");
      return;
    }

    const loadedGraphs = preset.graphs
      .map((graph, graphIndex) => ({
        id: `graph-${Date.now()}-${graphIndex}`,
        normalizeByReference: graph.normalizeByReference,
        datasets: graph.datasets
          .map<GraphDataset | null>((dataset) => {
            const base = availableDatasets.find((candidate) => candidate.id === dataset.id);
            if (!base) return null;
            return {
              ...base,
              instanceId: createDatasetInstanceId(base.id),
              color: dataset.color,
              ...(dataset.lineStyle ? { lineStyle: dataset.lineStyle } : {}),
              ...(dataset.pointStyle ? { pointStyle: dataset.pointStyle } : {}),
            };
          })
          .filter((dataset): dataset is GraphDataset => dataset !== null),
        creation: graph.creation || {
          datasetIds: graph.datasets.map((dataset) => dataset.id),
          datasetNames: graph.datasets.map((dataset) => dataset.name),
          datasetOrigins: Object.fromEntries(
            graph.datasets.map((dataset) => [
              dataset.id,
              {
                dateFilterMode: dataset.dateFilterMode,
                selectedPreset: dataset.selectedPreset,
                appliedDateRange: dataset.appliedDateRange || {},
              },
            ])
          ),
        },
      }))
      .filter((graph) => graph.datasets.length > 0);

    setGraphs(loadedGraphs as TGraph[]);
    setPresetActionError(null);
  };

  useEffect(() => {
    loadStoredPresets();
  }, [userId]);

  return {
    storedPresets,
    selectedPresetId,
    setSelectedPresetId,
    isLoadingPresets,
    presetActionError,
    isSavingPreset,
    isPresetModalOpen,
    presetModalMode,
    presetNameDraft,
    setPresetNameDraft,
    createPresetInBackend,
    saveSelectedPreset,
    renameSelectedPreset,
    requestDeleteSelectedPreset,
    submitPresetModal,
    closePresetModal,
    loadSelectedPreset,
  };
}