/**
 * Copyright 2022 CS GROUP - France, http://www.c-s.fr
 * All rights reserved
 */

import * as React from 'react';
import {
  NotebookActions,
  INotebookTracker,
  Notebook,
  INotebookModel
} from '@jupyterlab/notebook';
import { CodeCellModel } from '@jupyterlab/cells';
import { showErrorMessage } from '@jupyterlab/apputils';
import { concat, get } from 'lodash';
import { FormComponent } from './FormComponent';
import ModalComponent from './ModalComponent';
import formatCode from './CodeGenerator';
import SearchService from './SearchService';
import { IFormInput } from './types';
import { ServerConnection } from '@jupyterlab/services';
import { EODAG_SETTINGS_ADDRESS } from './config';
import { URLExt } from '@jupyterlab/coreutils';
import { useFetchUserSettings } from './hooks/useFetchData';
import { CarbonSettings, IcBaselineRefresh } from './icones';
import { Tooltip, PlacesType, VariantType } from 'react-tooltip';

export interface IProps {
  tracker: INotebookTracker;
  commands: any;
}

export interface IState {
  features: any;
  openDialog: any;
  searching: any;
  formValues: IFormInput;
  replaceCellIndex: number;
  isLoading: boolean;
  reloadIndicator: boolean;
}

const tooltipDark: VariantType = 'dark';
const tooltipBottom: PlacesType = 'bottom';

export class EodagBrowser extends React.Component<IProps, IState> {
  constructor(props: IProps) {
    super(props);
    this.state = {
      features: {},
      openDialog: false,
      searching: false,
      formValues: undefined,
      replaceCellIndex: undefined,
      isLoading: false,
      reloadIndicator: false
    };
    this.reloadUserSettings = this.reloadUserSettings.bind(this);
  }

  handleCurrentWidgetError = () => {
    if (!this.props.tracker.currentWidget) {
      showErrorMessage('No active notebook', 'Please open a notebook first');

      return;
    }
    return true;
  };

  handleShowFeature = (features: any, openModal: boolean) => {
    this.setState({
      features,
      openDialog: openModal
    });
  };

  handleRetrieveMoreFeature = async () => {
    const { features } = this.state;
    this.setState({
      searching: true
    });
    return SearchService.search(
      get(features, 'properties.page', 1) + 1,
      this.state.formValues
    )
      .then(results => {
        const featureList = concat(
          get(features, 'features', []),
          results.features
        );
        this.setState({
          searching: false,
          features: {
            ...results,
            features: featureList
          }
        });
      })
      .catch(() => {
        //display error
        this.setState({
          searching: false
        });
      });
  };

  isRetrievingMoreFeature = () => {
    return this.state.searching;
  };

  getCodeCell = (code: string) => {
    return new CodeCellModel({
      cell: {
        cell_type: 'code',
        metadata: {
          trusted: false,
          collapsed: false,
          tags: ['Injected by EODAG plugin']
        },
        source: [code]
      }
    });
  };

  getEodagSettings = async () => {
    const _serverSettings = ServerConnection.makeSettings();
    const _eodag_settings = URLExt.join(
      _serverSettings.baseUrl,
      _serverSettings.appUrl,
      `${EODAG_SETTINGS_ADDRESS}`
    );
    return fetch(URLExt.join(_eodag_settings), {
      credentials: 'same-origin'
    })
      .then(res => {
        if (res.status !== 200) {
          throw new Error('Bad response from server');
        }
        return res.json();
      })
      .then(data => {
        const { replaceCode } = data.settings;
        return replaceCode;
      });
  };

  handleCellInsertionPosition = (
    notebook: Notebook,
    model: INotebookModel,
    cell: CodeCellModel,
    replaceCode: boolean
  ) => {
    const activeCellIndex = notebook.activeCellIndex;
    const cells = notebook.widgets;
    const searchString = '# Code generated by eodag-labextension,';
    const isReplaceCellExist =
      cells.filter(cell => cell.node.innerText.includes(searchString)).length >
      0
        ? true
        : false;

    if (cells.length > 0) {
      cells.forEach((cell, index) => {
        if (cell.node.innerText.includes(searchString)) {
          this.setState({
            replaceCellIndex: index
          });
        }
      });
    }

    if (replaceCode && isReplaceCellExist) {
      notebook.activeCellIndex = this.state.replaceCellIndex;
      NotebookActions.deleteCells(notebook);
      model.cells.insert(this.state.replaceCellIndex, cell);
      notebook.activeCellIndex = this.state.replaceCellIndex;
    }

    if (replaceCode && !isReplaceCellExist) {
      this.setState({
        replaceCellIndex: activeCellIndex + 1
      });
      model.cells.insert(activeCellIndex + 1, cell);
      NotebookActions.selectBelow(notebook);
    }

    if (!replaceCode) {
      model.cells.insert(activeCellIndex + 1, cell);
      NotebookActions.selectBelow(notebook);
    }
  };

  handleGenerateQuery = async () => {
    this.setState({
      openDialog: false
    });

    if (!this.props.tracker.currentWidget) {
      return;
    }

    const notebook = this.props.tracker.currentWidget.content;
    const model = notebook.model;

    while (!model.defaultKernelLanguage) {
      await new Promise(resolve => setTimeout(resolve, 100));
    }

    if (model.defaultKernelLanguage !== 'python') {
      showErrorMessage(
        'Active notebook uses wrong kernel language. Only python is supported',
        {}
      );
      return;
    }

    if (model.readOnly) {
      showErrorMessage('Unable to inject cell into read-only notebook', {});
      return;
    }

    const replaceCode = await this.getEodagSettings();
    const code = formatCode(this.state.formValues, replaceCode);
    const cell = this.getCodeCell(code);

    this.handleCellInsertionPosition(notebook, model, cell, replaceCode);
  };

  handleCloseModal = () => {
    this.setState({
      openDialog: false
    });
  };

  handleOpenSettings = (): void => {
    this.props.commands.execute('settingeditor:open', { query: 'EODAG' });
  };

  updateLoadingState = () => {
    this.setState(prevState => ({
      isLoading: !prevState.isLoading,
      reloadIndicator: !prevState.reloadIndicator
    }));
  };

  reloadUserSettings = () => {
    useFetchUserSettings();
    this.updateLoadingState();
  };

  resetIsLoading = () => {
    this.updateLoadingState();
  };

  render() {
    const { openDialog, features } = this.state;
    return (
      <div className="jp-EodagWidget-products-search">
        <div className="jp-EodagWidget-header-wrapper">
          <header className="jp-EodagWidget-header">Products search</header>
          <div className="jp-EodagWidget-settings-wrapper">
            <div>
              <button
                type="button"
                className={'jp-EodagWidget-settingsbutton'}
                data-tooltip-id="eodag-setting"
                data-tooltip-content="Reload eodag environment"
                data-tooltip-variant={tooltipDark}
                data-tooltip-place={tooltipBottom}
                onClick={this.reloadUserSettings}
              >
                <IcBaselineRefresh
                  height="20"
                  width="20"
                  className={this.state.isLoading ? 'spin-icon' : ''}
                />
                <Tooltip id="eodag-setting" className="jp-Eodag-tooltip" />
              </button>
            </div>
            <div>
              <button
                type="button"
                className="jp-EodagWidget-settingsbutton"
                data-tooltip-id="eodag-setting"
                data-tooltip-content="Eodag labextension settings"
                data-tooltip-variant={tooltipDark}
                data-tooltip-place={tooltipBottom}
                onClick={this.handleOpenSettings}
              >
                <CarbonSettings height="20" width="20" />
                <Tooltip id="eodag-setting" className="jp-Eodag-tooltip" />
              </button>
            </div>
          </div>
        </div>
        <FormComponent
          isNotebookCreated={this.handleCurrentWidgetError}
          handleShowFeature={this.handleShowFeature}
          saveFormValues={(formValues: IFormInput) =>
            this.setState({ formValues })
          }
          handleGenerateQuery={this.handleGenerateQuery}
          reloadIndicator={this.state.reloadIndicator}
          onFetchComplete={this.resetIsLoading}
        />
        <ModalComponent
          open={openDialog}
          features={features}
          handleClose={this.handleCloseModal}
          handleGenerateQuery={this.handleGenerateQuery}
          isRetrievingMoreFeature={this.isRetrievingMoreFeature}
          handleRetrieveMoreFeature={this.handleRetrieveMoreFeature}
        />
      </div>
    );
  }
}
