// Licensed to Cloudera, Inc. under one
// or more contributor license agreements.  See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership.  Cloudera, Inc. licenses this file
// to you under the Apache License, Version 2.0 (the
// "License"); you may not use this file except in compliance
// with the License.  You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

import * as ko from 'knockout';

import componentUtils from './componentUtils';
import huePubSub from '../../utils/huePubSub';
import html2text from '../../utils/html/html2text';
import I18n from '../../utils/i18n';

export const NAME = 'field-samples';

const TEMPLATE = `
  <div class="context-popover-inline-autocomplete" style="display: flex">
    <div class="context-popover-sample-filter">
      <!-- ko component: {
        name: 'inline-autocomplete',
        params: {
          querySpec: querySpec,
          autocompleteFromEntries: autocompleteFromEntries
        }
      } --><!-- /ko -->
    </div>
    <!-- ko if: showOperations -->
    <div class="context-popover-sample-controls">
      <div class="margin-left-10 inline-block" data-bind="component: { name: 'hue-drop-down', params: { value: operation, entries: operations } }"></div>
      <div class="margin-left-10 inactive-action inline-block">
        <!-- ko if: loadingSamples() || refreshSampleEnabled() -->
        <a href="javascript:void(0);" data-bind="click: function () { refreshSampleEnabled(false); cancelRunningQueries() }"><i class="fa fa-stop"></i></a>
        <!-- /ko -->
        <!-- ko if: !loadingSamples() && !refreshSampleEnabled() -->
        <a href="javascript:void(0);" data-bind="click: function () { loadSamples(true) }"><i class="fa fa-play"></i></a>
        <!-- /ko -->
      </div>
    </div>
    <!-- /ko -->
  </div>

  <table class="table table-condensed table-nowrap">
    <thead>
    <tr>
      <th>${I18n('Sample')}</th>
    </tr>
    </thead>
    <!-- ko if: loadingSamples() && (!refreshSampleEnabled() || (refreshSampleEnabled() && columnSamples().length === 0))  -->
    <tbody>
    <tr>
      <td><!-- ko hueSpinner: { spin: true, inline: true } --><!-- /ko --></td>
    </tr>
    </tbody>
    <!-- /ko -->

    <!-- ko if: !loadingSamples() || (refreshSampleEnabled() && columnSamples().length > 0)  -->
    <tbody data-bind="foreach: filteredColumnSamples">
    <tr>
      <!-- ko if: typeof $parent.onSampleClick === 'function' -->
      <td class="sample-column pointer" data-bind="html: $data, attr: { 'title': hueUtils.html2text($data) }, click: $parent.sampleClick"></td>
      <!-- /ko -->
      <!-- ko ifnot: typeof $parent.onSampleClick === 'function' -->
      <td class="sample-column" data-bind="html: $data, attr: { 'title': hueUtils.html2text($data) }"></td>
      <!-- /ko -->
    </tr>
    </tbody>
    <!-- ko if: filteredColumnSamples().length === 0 -->
    <tbody>
    <tr>
      <!-- ko ifnot: hasErrors -->
      <td style="font-style: italic;">${I18n('No entries found')}</td>
      <!-- /ko -->
      <!-- ko if: hasErrors -->
      <td style="font-style: italic;">${I18n('Error loading samples')}</td>
      <!-- /ko -->
    </tr>
    </tbody>
    <!-- /ko -->
    <!-- /ko -->
  </table>
`;

class FieldSamples {
  constructor(params) {
    const self = this;
    self.catalogEntry = ko.isObservable(params.catalogEntry)
      ? params.catalogEntry
      : ko.observable(params.catalogEntry);
    self.onSampleClick = params.onSampleClick;
    self.refreshSampleInterval = params.refreshSampleInterval;
    self.refreshSampleTimeout = -1;
    self.refreshSampleEnabled = ko.observable(!!self.refreshSampleInterval);

    self.cancellablePromises = [];
    self.querySpec = ko.observable();

    self.hasErrors = ko.observable();
    self.loadingSamples = ko.observable();

    self.showOperations =
      !self.catalogEntry().isTemporary &&
      (self.catalogEntry().getDialect() === 'impala' ||
        self.catalogEntry().getDialect() === 'hive');

    self.sampleClick = function (html) {
      self.onSampleClick(html2text(html));
      huePubSub.publish('context.popover.hide');
    };

    self.operations = [
      {
        label: I18n('default'),
        type: 'default'
      },
      {
        label: I18n('distinct'),
        type: 'distinct'
      },
      {
        label: I18n('max'),
        type: 'max'
      },
      {
        label: I18n('min'),
        type: 'min'
      }
    ];

    self.operation = ko.observable(self.operations[0]);

    self.operation.subscribe(() => {
      self.loadSamples();
    });

    self.columnSamples = ko.observableArray();

    self.filteredColumnSamples = ko.pureComputed(() => {
      if (!self.querySpec() || self.querySpec().query === '') {
        return self.columnSamples();
      }

      return self.columnSamples().filter(sampleValue => {
        if (typeof sampleValue === 'undefined' || sampleValue === null) {
          return false;
        }
        return self.querySpec().text.every(text => {
          const textLower = text.toLowerCase();
          return sampleValue.toString().toLowerCase().indexOf(textLower) !== -1;
        });
      });
    });

    self.autocompleteFromEntries = function (nonPartial, partial) {
      const result = [];
      const partialLower = partial.toLowerCase();
      self.columnSamples().forEach(sample => {
        if (sample[0].toString().toLowerCase().indexOf(partialLower) === 0) {
          result.push(nonPartial + partial + sample[0].toString().substring(partial.length));
        }
      });
      return result;
    };

    self.loadSamples();
  }

  loadSamples(refreshCache) {
    const self = this;
    window.clearTimeout(self.refreshSampleTimeout);
    self.cancelRunningQueries();
    self.loadingSamples(true);
    const samplePromise = self.catalogEntry().getSample({
      silenceErrors: true,
      cancellable: true,
      refreshCache: refreshCache,
      operation: self.operation().type
    });
    self.cancellablePromises.push(samplePromise);
    samplePromise
      .then(samples => {
        if (samples.data && samples.data.length) {
          self.columnSamples(samples.data);
        }
      })
      .catch(() => {
        self.hasErrors(true);
      })
      .finally(() => {
        self.loadingSamples(false);
        if (self.refreshSampleTimeout && self.refreshSampleEnabled()) {
          self.refreshSampleTimeout = window.setTimeout(() => {
            self.loadSamples(true);
          }, self.refreshSampleInterval);
        }
      });
  }

  cancelRunningQueries() {
    const self = this;
    window.clearTimeout(self.refreshSampleTimeout);
    while (self.cancellablePromises.length) {
      const promise = self.cancellablePromises.pop();
      if (promise.cancel) {
        promise.cancel();
      }
    }
  }

  dispose() {
    const self = this;
    self.cancelRunningQueries();
  }
}

componentUtils.registerComponent(NAME, FieldSamples, TEMPLATE);
