import * as React from "react";

import {
  useElementSize,
  useImmerState,
  useDataItems,
  WidgetContextInterface,
  WidgetBaseContextInterface,
  WidgetConfigError,
  useTranslation,
  DataItemInterface,
  useOpenDashServices,
  DataItemHistoryOptionsInterface,
  SourceInterface,
  useSource,
  DataItemValueInterface,
} from "..";
import { DataItemIdentifierInterface } from "../interfaces/DataItemIdentifierInterface";
import { DataItemDimensionIdentifierInterface } from "../interfaces/DataItemDimensionIdentifierInterface";

export function useWidgetContextSetup(
  context: WidgetBaseContextInterface
): WidgetContextInterface {
  const [t] = useTranslation(["opendash"]);
  const { DashboardService } = useOpenDashServices();

  const [draft, updateDraft, replaceDraft, assignToDraft] = useImmerState({});

  React.useEffect(() => {
    if (context?.widget?.config) {
      replaceDraft(context?.widget?.config);
    }
  }, [context?.widget?.config]);

  function saveDraft() {
    DashboardService.updateWidget({
      ...context.widget,
      id: context.id,
      config: draft,
    });
  }

  function setLoading(loading: boolean) {
    context.setState({ loading });
  }

  function useContainerSize(): {
    width: number;
    height: number;
  } {
    return useElementSize(context.container, 250);
  }

  function useSourceConfig(): SourceInterface[] {
    const type = context?.type.dataItems;
    const config = context?.widget.config._sources;
    const [, , allSources] = useSource();

    if (!type) {
      throw new WidgetConfigError(
        t("monitoring.widgets.config_error.sources_unsupported")
      );
    }

    if (type.select !== "source") {
      throw new WidgetConfigError(
        t("monitoring.widgets.config_error.sources_dev_bad_config")
      );
    }

    if (!config) {
      throw new WidgetConfigError(
        t("monitoring.widgets.config_error.sources_notfound")
      );
    }

    if (config.length > type.max) {
      throw new WidgetConfigError(
        t("monitoring.widgets.config_error.sources_max")
      );
    }

    if (config.length < type.min) {
      throw new WidgetConfigError(
        t("monitoring.widgets.config_error.sources_min")
      );
    }

    const sources: SourceInterface[] = config.map(id =>
      allSources.find(source => source.id === id)
    );

    for (const source of sources) {
      if (!source) {
        throw new WidgetConfigError(
          t("monitoring.widgets.config_error.sources_missing")
        );
      }
    }

    return sources;
  }

  function useItemConfig(): DataItemInterface[] {
    const type = context?.type.dataItems;
    const config = context?.widget.config._items;
    const allItems = useDataItems();

    if (!type) {
      throw new WidgetConfigError(
        t("monitoring.widgets.config_error.items_unsupported")
      );
    }

    if (type.select !== "item") {
      throw new WidgetConfigError(
        t("monitoring.widgets.config_error.items_dev_bad_config")
      );
    }

    if (!config) {
      throw new WidgetConfigError(
        t("monitoring.widgets.config_error.items_notfound")
      );
    }

    if (config.length > type.max) {
      throw new WidgetConfigError(
        t("monitoring.widgets.config_error.items_max")
      );
    }

    if (config.length < type.min) {
      throw new WidgetConfigError(
        t("monitoring.widgets.config_error.items_min")
      );
    }

    const items: DataItemInterface[] = config.map(([source, id]) =>
      allItems.find(item => item.id === id && item.source === source)
    );

    for (const item of items) {
      if (!item) {
        throw new WidgetConfigError(
          t("monitoring.widgets.config_error.items_missing")
        );
      }
    }

    return items;
  }

  function useItemDimensionConfig(): [DataItemInterface, number][] {
    const type = context?.type.dataItems;
    const config = context?.widget.config._dimensions;
    const allItems = useDataItems();

    if (!type) {
      throw new WidgetConfigError(
        t("monitoring.widgets.config_error.items_unsupported")
      );
    }

    if (type.select !== "dimension") {
      throw new WidgetConfigError(
        t("monitoring.widgets.config_error.items_dev_bad_config")
      );
    }

    if (!config) {
      throw new WidgetConfigError(
        t("monitoring.widgets.config_error.items_notfound")
      );
    }

    if (config.length > type.max) {
      throw new WidgetConfigError(
        t("monitoring.widgets.config_error.items_max")
      );
    }

    if (config.length < type.min) {
      throw new WidgetConfigError(
        t("monitoring.widgets.config_error.items_min")
      );
    }

    const items: [
      DataItemInterface,
      number
    ][] = config.map(([source, id, dimension]) => [
      allItems.find(item => item.id === id && item.source === source),
      dimension,
    ]);

    for (const [item, dimension] of items) {
      if (!item) {
        throw new WidgetConfigError(
          t("monitoring.widgets.config_error.items_missing")
        );
      }

      if (!type.types.includes(item.valueTypes[dimension].type)) {
        throw new WidgetConfigError(
          t("monitoring.widgets.config_error.items_type")
        );
      }
    }

    return items;
  }

  function useFetchHistory(
    overwriteOptions: DataItemHistoryOptionsInterface = {}
  ): DataItemValueInterface[] {
    const allItems = useDataItems();

    const itemsToFetch = React.useMemo(() => {
      const config: (
        | DataItemIdentifierInterface
        | DataItemDimensionIdentifierInterface
      )[] = [
        ...(context?.widget.config._items || []),
        ...(context?.widget.config._dimensions || []),
      ];

      return config.map(([source, id]) =>
        allItems.find(item => item.source === source && item.id === id)
      );
    }, [allItems, context?.widget.config]);

    return [];
  }

  function useHistoryConfig(): DataItemHistoryOptionsInterface {
    const type = context?.type.dataHistory;
    const config = context?.widget.config._history;

    if (!type) {
      throw new WidgetConfigError(
        t("monitoring.widgets.config_error.history_unsupported")
      );
    }

    if (!config) {
      throw new WidgetConfigError(
        t("monitoring.widgets.config_error.history_notfound")
      );
    }

    return context?.widget?.config?._history;
  }

  return React.useMemo(
    () => ({
      config: context?.widget.config,
      draft,
      updateDraft,
      replaceDraft,
      assignToDraft,
      saveDraft,
      setLoading,
      useContainerSize,
      useSourceConfig,
      useItemConfig,
      useItemDimensionConfig,
      useHistoryConfig,
    }),
    [context, draft]
  );
}
