import React, { lazy, Suspense } from "react";

import type { WidgetProps, WidgetState } from "widgets/BaseWidget";
import BaseWidget from "widgets/BaseWidget";
import Skeleton from "components/utils/Skeleton";
import { retryPromise } from "utils/AppsmithUtils";
import { EventType } from "constants/AppsmithActionConstants/ActionConstants";
import { contentConfig, styleConfig } from "./propertyConfig";
import {
  CUSTOM_ECHART_FEATURE_FLAG,
  DefaultEChartConfig,
  DefaultEChartsBasicChartsData,
  DefaultFusionChartConfig,
  FUSION_CHART_DEPRECATION_FLAG,
  messages,
} from "../constants";
import type { ChartSelectedDataPoint } from "../constants";
import type { ChartComponentProps } from "../component";
import { Colors } from "constants/Colors";
import type { Stylesheet } from "entities/AppTheming";
import { DefaultAutocompleteDefinitions } from "widgets/WidgetUtils";
import type {
  AnvilConfig,
  AutocompletionDefinitions,
  WidgetCallout,
} from "WidgetProvider/constants";
import { ChartErrorComponent } from "../component/ChartErrorComponent";
import { syntaxErrorsFromProps } from "./SyntaxErrorsEvaluation";
import { EmptyChartData } from "../component/EmptyChartData";
import { FILL_WIDGET_MIN_WIDTH } from "constants/minWidthConstants";
import {
  FlexVerticalAlignment,
  ResponsiveBehavior,
} from "layoutSystems/common/utils/constants";
import { generateReactKey } from "widgets/WidgetUtils";
import { LabelOrientation } from "../constants";
import IconSVG from "../icon.svg";
import { WIDGET_TAGS } from "constants/WidgetConstants";
import { EChartsDatasetBuilder } from "../component/EChartsDatasetBuilder";

const ChartComponent = lazy(async () =>
  retryPromise(
    async () => import(/* webpackChunkName: "charts" */ "../component"),
  ),
);

export const emptyChartData = (props: ChartWidgetProps) => {
  if (props.chartType == "CUSTOM_FUSION_CHART") {
    return Object.keys(props.customFusionChartConfig).length == 0;
  } else if (props.chartType == "CUSTOM_ECHART") {
    return Object.keys(props.customEChartConfig).length == 0;
  } else {
    const builder = new EChartsDatasetBuilder(props.chartType, props.chartData);

    for (const seriesID in builder.filteredChartData) {
      if (Object.keys(props.chartData[seriesID].data).length > 0) {
        return false;
      }
    }
    return true;
  }
};

class ChartWidget extends BaseWidget<ChartWidgetProps, WidgetState> {
  static type = "CHART_WIDGET";

  static getConfig() {
    return {
      name: "Chart",
      iconSVG: IconSVG,
      tags: [WIDGET_TAGS.DISPLAY],
      needsMeta: true,
      searchTags: ["graph", "visuals", "visualisations"],
    };
  }

  static getDefaults() {
    return {
      rows: 32,
      columns: 24,
      widgetName: "Chart",
      chartType: "COLUMN_CHART",
      chartName: "Sales Report",
      allowScroll: false,
      version: 1,
      animateLoading: true,
      responsiveBehavior: ResponsiveBehavior.Fill,
      flexVerticalAlignment: FlexVerticalAlignment.Top,
      minWidth: FILL_WIDGET_MIN_WIDTH,
      showDataPointLabel: false,
      customEChartConfig: `{{\n${JSON.stringify(
        DefaultEChartConfig,
        null,
        2,
      )}\n}}`,
      chartData: {
        [generateReactKey()]: DefaultEChartsBasicChartsData,
      },
      xAxisName: "Product Line",
      yAxisName: "Revenue($)",
      labelOrientation: LabelOrientation.AUTO,
      customFusionChartConfig: DefaultFusionChartConfig,

      /**
       * TODO, @sbalaji92
       * need to remove this once widget properties get added to dynamic binding path list
       * in WidgetAdditionSagas/dynamicBindingPathList function
       * */
      dynamicBindingPathList: [{ key: "customEChartConfig" }],
    };
  }

  static getAutoLayoutConfig() {
    return {
      widgetSize: [
        {
          viewportMinWidth: 0,
          configuration: () => {
            return {
              minWidth: "280px",
              minHeight: "300px",
            };
          },
        },
      ],
    };
  }

  static getAnvilConfig(): AnvilConfig | null {
    return {
      widgetSize: {
        maxHeight: {},
        maxWidth: {},
        minHeight: { base: "300px" },
        minWidth: { base: "280px" },
      },
    };
  }

  static getMethods() {
    return {
      getEditorCallouts(props: WidgetProps): WidgetCallout[] {
        const callouts: WidgetCallout[] = [];
        if (
          ChartWidget.showCustomFusionChartDeprecationMessages() &&
          props.chartType == "CUSTOM_FUSION_CHART"
        ) {
          callouts.push({
            message: messages.customFusionChartDeprecationMessage,
            links: [
              {
                text: "Learn more",
                url: "https://docs.appsmith.com",
              },
            ],
          });
        }
        return callouts;
      },
    };
  }

  static getAutocompleteDefinitions(): AutocompletionDefinitions {
    return {
      "!doc":
        "Chart widget is used to view the graphical representation of your data. Chart is the go-to widget for your data visualisation needs.",
      "!url": "https://docs.appsmith.com/widget-reference/chart",
      isVisible: DefaultAutocompleteDefinitions.isVisible,
      chartData: {
        seriesName: "string",
        data: "[$__chartDataPoint__$]",
      },
      xAxisName: "string",
      yAxisName: "string",
      selectedDataPoint: "$__chartDataPoint__$",
    };
  }

  static getMetaPropertiesMap(): Record<string, any> {
    return {
      selectedDataPoint: undefined,
    };
  }

  static getPropertyPaneContentConfig() {
    return contentConfig(
      this.getFeatureFlag(CUSTOM_ECHART_FEATURE_FLAG),
      this.showCustomFusionChartDeprecationMessages(),
    );
  }

  static getPropertyPaneStyleConfig() {
    return styleConfig;
  }

  static showCustomFusionChartDeprecationMessages() {
    return this.getFeatureFlag(FUSION_CHART_DEPRECATION_FLAG);
  }

  static getStylesheetConfig(): Stylesheet {
    return {
      borderRadius: "{{appsmith.theme.borderRadius.appBorderRadius}}",
      boxShadow: "{{appsmith.theme.boxShadow.appBoxShadow}}",
      accentColor: "{{appsmith.theme.colors.primaryColor}}",
      fontFamily: "{{appsmith.theme.fontFamily.appFont}}",
    };
  }

  onDataPointClick = (selectedDataPoint: ChartSelectedDataPoint) => {
    this.props.updateWidgetMetaProperty(
      "selectedDataPoint",
      selectedDataPoint,
      {
        triggerPropertyName: "onDataPointClick",
        dynamicString: this.props.onDataPointClick,
        event: {
          type: EventType.ON_DATA_POINT_CLICK,
        },
      },
    );
  };

  getWidgetView() {
    const errors = syntaxErrorsFromProps(this.props);

    if (errors.length == 0) {
      if (emptyChartData(this.props)) {
        return <EmptyChartData />;
      } else {
        return (
          <Suspense fallback={<Skeleton />}>
            <ChartComponent
              allowScroll={this.props.allowScroll}
              borderRadius={this.props.borderRadius}
              boxShadow={this.props.boxShadow}
              chartData={this.props.chartData}
              chartName={this.props.chartName}
              chartType={this.props.chartType}
              customEChartConfig={this.props.customEChartConfig}
              customFusionChartConfig={this.props.customFusionChartConfig}
              dimensions={this.props}
              fontFamily={this.props.fontFamily ?? "Nunito Sans"}
              hasOnDataPointClick={Boolean(this.props.onDataPointClick)}
              isLoading={this.props.isLoading}
              isVisible={this.props.isVisible}
              key={this.props.widgetId}
              labelOrientation={this.props.labelOrientation}
              onDataPointClick={this.onDataPointClick}
              primaryColor={this.props.accentColor ?? Colors.ROYAL_BLUE_2}
              setAdaptiveYMin={this.props.setAdaptiveYMin}
              showDataPointLabel={this.props.showDataPointLabel}
              widgetId={this.props.widgetId}
              xAxisName={this.props.xAxisName}
              yAxisName={this.props.yAxisName}
            />
          </Suspense>
        );
      }
    } else {
      return <ChartErrorComponent error={errors[0]} />;
    }
  }
}

type ChartComponentPartialProps = Omit<ChartComponentProps, "onDataPointClick">;
export interface ChartWidgetProps
  extends WidgetProps,
    ChartComponentPartialProps {
  onDataPointClick?: string;
}

export default ChartWidget;
