/*
 * Copyright OpenSearch Contributors
 * SPDX-License-Identifier: Apache-2.0
 */

import { i18n } from '@osd/i18n';
import { EuiEmptyPrompt, EuiFlexGroup, EuiFlexItem, EuiIcon, EuiPanel } from '@elastic/eui';
import React, { useState, useMemo, useEffect, useLayoutEffect } from 'react';
import { useOpenSearchDashboards } from '../../../../opensearch_dashboards_react/public';
import { IExpressionLoaderParams } from '../../../../expressions/public';
import { VisBuilderServices } from '../../types';
import { validateSchemaState, validateAggregations } from '../utils/validations';
import { useTypedDispatch, useTypedSelector, setUIStateState } from '../utils/state_management';
import { useAggs, useVisualizationType } from '../utils/use';
import { PersistedState } from '../../../../visualizations/public';

import hand_field from '../../assets/hand_field.svg';
import fields_bg from '../../assets/fields_bg.svg';

import './workspace.scss';
import { ExperimentalInfo } from './experimental_info';
import { handleVisEvent } from '../utils/handle_vis_event';

export const WorkspaceUI = () => {
  const {
    services: {
      expressions: { ReactExpressionRenderer },
      notifications: { toasts },
      data,
      uiActions,
    },
  } = useOpenSearchDashboards<VisBuilderServices>();
  const { toExpression, ui } = useVisualizationType();
  const { aggConfigs, indexPattern } = useAggs();
  const [expression, setExpression] = useState<string>();
  const [searchContext, setSearchContext] = useState<IExpressionLoaderParams['searchContext']>({
    query: data.query.queryString.getQuery(),
    filters: data.query.filterManager.getFilters(),
    timeRange: data.query.timefilter.timefilter.getTime(),
  });
  const rootState = useTypedSelector((state) => state);
  const dispatch = useTypedDispatch();
  // Visualizations require the uiState object to persist even when the expression changes
  // eslint-disable-next-line react-hooks/exhaustive-deps
  const uiState = useMemo(() => new PersistedState(rootState.ui), []);

  useEffect(() => {
    if (rootState.metadata.editor.state === 'loaded') {
      uiState.setSilent(rootState.ui);
    }
    // To update uiState once saved object data is loaded
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, [rootState.metadata.editor.state, uiState]);

  useEffect(() => {
    uiState.on('change', (args) => {
      // Store changes to UI state
      dispatch(setUIStateState(uiState.toJSON()));
    });
  }, [dispatch, uiState]);

  useEffect(() => {
    async function loadExpression() {
      const schemas = ui.containerConfig.data.schemas;

      const noAggs = (aggConfigs?.aggs?.length ?? 0) === 0;
      const schemaValidation = validateSchemaState(schemas, rootState.visualization);
      const aggValidation = validateAggregations(aggConfigs?.aggs || []);

      if (!aggValidation.valid || !schemaValidation.valid) {
        setExpression(undefined);
        if (noAggs) return; // don't show error when there are no active aggregations

        const err = schemaValidation.errorMsg || aggValidation.errorMsg;

        if (err)
          toasts.addWarning({
            id: 'vb_expression_validation',
            title: err,
          });

        return;
      }

      const exp = await toExpression(rootState, searchContext);
      setExpression(exp);
    }

    loadExpression();
  }, [rootState, toExpression, toasts, ui.containerConfig.data.schemas, searchContext, aggConfigs]);

  useLayoutEffect(() => {
    const subscription = data.query.state$.subscribe(({ state }) => {
      setSearchContext({
        query: state.query,
        timeRange: state.time,
        filters: state.filters,
      });
    });

    return () => {
      subscription.unsubscribe();
    };
  }, [data.query.state$]);

  return (
    <section className="vbWorkspace">
      <EuiFlexGroup className="vbCanvasControls">
        <EuiFlexItem>
          <ExperimentalInfo />
        </EuiFlexItem>
      </EuiFlexGroup>
      <EuiPanel className="vbCanvas" data-test-subj="visualizationLoader">
        {expression ? (
          <ReactExpressionRenderer
            expression={expression}
            searchContext={searchContext}
            uiState={uiState}
            onEvent={(event) => handleVisEvent(event, uiActions, indexPattern?.timeFieldName)}
          />
        ) : (
          <EuiFlexItem className="vbWorkspace__empty" data-test-subj="emptyWorkspace">
            <EuiEmptyPrompt
              title={
                <h2>
                  {i18n.translate('visBuilder.workSpace.empty.title', {
                    defaultMessage: 'Add a field to start',
                  })}
                </h2>
              }
              body={
                <>
                  <p>
                    {i18n.translate('visBuilder.workSpace.empty.description', {
                      defaultMessage:
                        'Drag a field to the configuration panel to generate a visualization.',
                    })}
                  </p>
                  <div className="vbWorkspace__container">
                    <EuiIcon className="vbWorkspace__fieldSvg" type={fields_bg} size="original" />
                    <EuiIcon
                      className="vbWorkspace__handFieldSvg"
                      type={hand_field}
                      size="original"
                    />
                  </div>
                </>
              }
            />
          </EuiFlexItem>
        )}
      </EuiPanel>
    </section>
  );
};

// The app uses EuiResizableContainer that triggers a rerender for every mouseover action.
// To prevent this child component from unnecessarily rerendering in that instance, it needs to be memoized
export const Workspace = React.memo(WorkspaceUI);