import { useLabClusterRegions } from "@/components/labs/hook";
import {
  type LabMetaV2,
  useLabInstance,
  useLabInstanceEnvironmentVariables,
  useLabInstanceParts,
  useLabInstanceResources,
  useLabMeta,
} from "@/components/labs/hooks-v2";
import {
  type ParsedSection,
  parseSectionsV2,
} from "@/components/labs/parse-section";
import { useLocaleFunction } from "@/hooks/use-localed";
import { toast } from "@/hooks/use-toast";
import type { LabMetaExtra, LabResourceItem } from "@/lib/api/labs";
import type { LabInstanceMeta, LabPart } from "@/lib/api/labs.v2";
import { useRouter } from "next/navigation";
import {
  createContext,
  type Dispatch,
  type PropsWithChildren,
  type SetStateAction,
  use,
  useMemo,
  useState,
  useTransition,
} from "react";

export type LabInstanceContextValues = {
  meta?: LabMetaV2 & LabMetaExtra;
  lab: LabInstanceMeta;
  parsedSections?: ParsedSection[];
  parts?: LabPart[];
  resources?: [{ resources: LabResourceItem[] }];
  environmentVariables?: Record<string, string | null>;
  navigating: boolean;
  step: number;
  onStepChange: (value: SetStateAction<number>) => void;
  isReady: boolean;
  error?: unknown;
  resourcesDialogOpen: boolean;
  onResourcesDialogOpenChanged: Dispatch<SetStateAction<boolean>>;
};

export const LabInstanceContext = createContext<LabInstanceContextValues>(
  null as never
);

export function LabInstanceProvider({
  id,
  labInstanceMeta: propMeta,
  step,
  children,
}: PropsWithChildren<{
  id: number;
  labInstanceMeta: LabInstanceMeta;
  step: number;
}>) {
  const t = useLocaleFunction();

  const labInstanceMeta = useLabInstance(id, propMeta, () => {
    toast({
      title: t("lab.alerts.succeed-to-start"),
    });
  });
  const partsQuery = useLabInstanceParts(id);
  const resources = useLabInstanceResources(labInstanceMeta.data);
  const environmentVariables = useLabInstanceEnvironmentVariables(
    labInstanceMeta.data
  );
  const meta = useLabMeta(labInstanceMeta.data.lab_code);

  const router = useRouter();
  const [navigating, startNavigation] = useTransition();
  const [resourcesDialogOpen, setResourcesDialogOpen] = useState(false);

  const onStepChange = (value: SetStateAction<number>) => {
    let nextStep: number;
    if (typeof value === "function") {
      nextStep = value(step);
    } else {
      nextStep = value;
    }
    startNavigation(() => {
      if (nextStep) {
        router.push(
          `/lab?instance=${labInstanceMeta.data.id}&step=${nextStep}`
        );
      } else {
        router.push(`/lab?instance=${labInstanceMeta.data.id}`);
      }
    });
  };

  const { data: clusterRegions } = useLabClusterRegions();

  const fallbackEnvironmentVariables = useMemo(() => {
    const instance = labInstanceMeta.data;
    const regionInfoData = clusterRegions
      ? clusterRegions[instance.cloud_provider][instance.region_name]
      : null;

    return {
      TIDB_VERSION: instance.tidb_version
        ? String(instance.tidb_version)
        : null,
      CLOUD_PROVIDER: instance.cloud_provider,
      REGION_CODE: regionInfoData?.["region-code"] ?? null,
      REGION_NAME: regionInfoData?.["region-name"] ?? null,
      TIDB_ENVIRONMENT: instance.tidb_environment,
    };
  }, [labInstanceMeta.data, clusterRegions]);

  const env = environmentVariables.data ?? fallbackEnvironmentVariables;
  // Only support to enable environment variables fallbacks when the lab is not running.
  const enableEnvironmentVariablesFallback =
    labInstanceMeta.data.status !== "running";

  const parsedSections = useMemo(() => {
    const parts = partsQuery.data;
    if (!parts) {
      return undefined;
    }

    return parseSectionsV2(
      t.locale,
      parts,
      env,
      false,
      enableEnvironmentVariablesFallback
    );
  }, [partsQuery.data, env, t.locale, enableEnvironmentVariablesFallback]);

  return (
    <LabInstanceContext
      value={{
        meta: meta.data,
        lab: labInstanceMeta.data,
        parts: partsQuery.data,
        resources: resources.data
          ? [{ resources: resources.data }]
          : resources.data,
        environmentVariables: env,
        step,
        onStepChange,
        navigating,
        isReady: labInstanceMeta.data.status === "running",
        error: labInstanceMeta.error,
        parsedSections,
        resourcesDialogOpen,
        onResourcesDialogOpenChanged: setResourcesDialogOpen,
      }}
    >
      {children}
    </LabInstanceContext>
  );
}

export function useLabInstanceContext() {
  return use(LabInstanceContext);
}
