/**
 * Copyright (c) HashiCorp, Inc.
 * SPDX-License-Identifier: MPL-2.0
 */

import { getOwner } from '@ember/application';
import Controller from '@ember/controller';
import { action } from '@ember/object';
import { tracked } from '@glimmer/tracking';
import { schedule } from '@ember/runloop';
import { inject as service } from '@ember/service';
import { useMachine } from 'ember-statecharts';
import { use } from 'ember-usable';
import evaluationsMachine from '../../machines/evaluations';

const ALL_NAMESPACE_WILDCARD = '*';

export default class EvaluationsController extends Controller {
  @service store;
  @service userSettings;

  // We use statecharts here to manage complex user flows for the sidebar logic
  @use
  statechart = useMachine(evaluationsMachine).withConfig({
    services: {
      loadEvaluation: this.loadEvaluation,
    },
    actions: {
      updateEvaluationQueryParameter: this.updateEvaluationQueryParameter,
      removeCurrentEvaluationQueryParameter:
        this.removeCurrentEvaluationQueryParameter,
    },
    guards: {
      sidebarIsOpen: this._sidebarIsOpen,
    },
  });

  queryParams = [
    'nextToken',
    'currentEval',
    'pageSize',
    'status',
    { qpNamespace: 'namespace' },
    'type',
    'searchTerm',
  ];
  @tracked currentEval = null;

  @action
  _sidebarIsOpen() {
    return !!this.currentEval;
  }

  @action
  async loadEvaluation(context, { evaluation }) {
    let evaluationId;
    if (evaluation?.id) {
      evaluationId = evaluation.id;
    } else {
      evaluationId = this.currentEval;
    }

    return this.store.findRecord('evaluation', evaluationId, {
      reload: true,
      adapterOptions: { related: true },
    });
  }

  @action
  async handleEvaluationClick(evaluation, e) {
    if (
      e instanceof MouseEvent ||
      (e instanceof KeyboardEvent &&
        (e.code === 'Enter' || e.code === 'Space')) ||
      !e
    ) {
      this.statechart.send('LOAD_EVALUATION', { evaluation });
    }
  }

  @action
  notifyEvalChange([evaluation]) {
    schedule('actions', this, () => {
      this.statechart.send('CHANGE_EVAL', { evaluation });
    });
  }

  @action
  updateEvaluationQueryParameter(context, { evaluation }) {
    this.currentEval = evaluation.id;
  }

  @action
  removeCurrentEvaluationQueryParameter() {
    this.currentEval = null;
  }

  get shouldDisableNext() {
    return !this.model.meta?.nextToken;
  }

  get shouldDisablePrev() {
    return !this.previousTokens.length;
  }

  get optionsEvaluationsStatus() {
    return [
      { key: null, label: 'All' },
      { key: 'blocked', label: 'Blocked' },
      { key: 'pending', label: 'Pending' },
      { key: 'complete', label: 'Complete' },
      { key: 'failed', label: 'Failed' },
      { key: 'canceled', label: 'Canceled' },
    ];
  }

  get optionsTriggeredBy() {
    return [
      { key: null, label: 'All' },
      { key: 'job-register', label: 'Job Register' },
      { key: 'job-deregister', label: 'Job Deregister' },
      { key: 'periodic-job', label: 'Periodic Job' },
      { key: 'node-drain', label: 'Node Drain' },
      { key: 'node-update', label: 'Node Update' },
      { key: 'alloc-stop', label: 'Allocation Stop' },
      { key: 'scheduled', label: 'Scheduled' },
      { key: 'rolling-update', label: 'Rolling Update' },
      { key: 'deployment-watcher', label: 'Deployment Watcher' },
      { key: 'failed-follow-up', label: 'Failed Follow Up' },
      { key: 'max-disconnect-timeout', label: 'Max Disconnect Timeout' },
      { key: 'max-plan-attempts', label: 'Max Plan Attempts' },
      { key: 'alloc-failure', label: 'Allocation Failure' },
      { key: 'queued-allocs', label: 'Queued Allocations' },
      { key: 'preemption', label: 'Preemption' },
      { key: 'job-scaling', label: 'Job Scalling' },
    ];
  }

  get optionsNamespaces() {
    const namespaces = this.store.peekAll('namespace').map((namespace) => ({
      key: namespace.name,
      label: namespace.name,
    }));

    // Create default namespace selection
    namespaces.unshift({
      key: ALL_NAMESPACE_WILDCARD,
      label: 'All (*)',
    });

    return namespaces;
  }

  get optionsType() {
    return [
      { key: null, label: 'All' },
      { key: 'client', label: 'Client' },
      { key: 'no client', label: 'No Client' },
    ];
  }

  filters = ['status', 'qpNamespace', 'type', 'triggeredBy', 'searchTerm'];

  get hasFiltersApplied() {
    return this.filters.reduce((result, filter) => {
      // By default we always set qpNamespace to the '*' wildcard
      // We need to ensure that if namespace is the only filter, that we send the correct error message to the user
      if (this[filter] && filter !== 'qpNamespace') {
        result = true;
      }
      return result;
    }, false);
  }

  get currentFilters() {
    const result = [];
    for (const filter of this.filters) {
      const isNamespaceWildcard =
        filter === 'qpNamespace' && this[filter] === '*';
      if (this[filter] && !isNamespaceWildcard) {
        result.push({ [filter]: this[filter] });
      }
    }
    return result;
  }

  get noMatchText() {
    let text = '';
    const cleanNames = {
      status: 'Status',
      qpNamespace: 'Namespace',
      type: 'Type',
      triggeredBy: 'Triggered By',
      searchTerm: 'Search Term',
    };
    if (this.hasFiltersApplied) {
      for (let i = 0; i < this.currentFilters.length; i++) {
        const filter = this.currentFilters[i];
        const [name] = Object.keys(filter);
        const filterName = cleanNames[name];
        const filterValue = filter[name];
        if (this.currentFilters.length === 1)
          return `${filterName}: ${filterValue}.`;
        if (i !== 0 && i !== this.currentFilters.length - 1)
          text = text.concat(`, ${filterName}: ${filterValue}`);
        if (i === 0) text = text.concat(`${filterName}: ${filterValue}`);
        if (i === this.currentFilters.length - 1) {
          return text.concat(`, ${filterName}: ${filterValue}.`);
        }
      }
    }

    return text;
  }

  @tracked pageSize = this.userSettings.pageSize;
  @tracked nextToken = null;
  @tracked previousTokens = [];
  @tracked status = null;
  @tracked triggeredBy = null;
  @tracked qpNamespace = ALL_NAMESPACE_WILDCARD;
  @tracked type = null;
  @tracked searchTerm = null;

  @action
  onChange(newPageSize) {
    this.pageSize = newPageSize;
  }

  @action
  onNext(nextToken) {
    this.previousTokens = [...this.previousTokens, this.nextToken];
    this.nextToken = nextToken;
  }

  @action
  onPrev() {
    const lastToken = this.previousTokens.pop();
    this.previousTokens = [...this.previousTokens];
    this.nextToken = lastToken;
  }

  @action
  refresh() {
    const isDefaultParams = this.nextToken === null && this.status === null;
    if (isDefaultParams) {
      getOwner(this).lookup('route:evaluations.index').refresh();
      return;
    }

    this._resetTokens();
    this.status = null;
    this.pageSize = this.userSettings.pageSize;
  }

  @action
  setQueryParam(qp, selection) {
    this._resetTokens();
    this[qp] = selection;
  }

  @action
  toggle() {
    this._resetTokens();
    this.shouldOnlyDisplayClientEvals = !this.shouldOnlyDisplayClientEvals;
  }

  @action
  _resetTokens() {
    this.nextToken = null;
    this.previousTokens = [];
  }
}
