import { html } from 'lit-element';
import { ApolloQuery } from '@apollo-elements/lit-apollo';
import moment from 'moment';

/* Utils */
import { getClient } from '@ppci/utils';
import { query } from './graphql';

/* Styles */
import style from './style';

/* Components */
import '@ppci/stacked-column-chart';

export class AllocationGraph extends ApolloQuery {
  static get properties() {
    return {
      deviceId: { type: String },
      direction: { type: String },
    };
  }

  constructor() {
    super();
    this.loading = true;
    this.fetchPolicy = 'no-cache';
    this.client = getClient();
    this.view = 0;
  }

  static get styles() {
    return style;
  }

  firstUpdated() {
    this.fetch();
    this.observableQuery.result().then((result) => {
      // Set current date from device endDate or current date
      if (!this.date) {
        const { startDate, endDate } = result.data.response.device;
        this.date = endDate ? moment(endDate).format('YYYY-MM-DD') : moment().subtract(1, 'days').format('YYYY-MM-DD');
        this.min = moment(startDate).format('YYYY-MM-DD');
        this.max = endDate ? moment(endDate).format('YYYY-MM-DD') : moment().subtract(1, 'days').format('YYYY-MM-DD');
        this.requestUpdate();
      }
    });
  }

  updated(changedProperties) {
    if (
      changedProperties.get('deviceId')
      || changedProperties.get('direction')
    ) {
      this.fetch();
    }
  }

  /* Override default Apollo Query should Update */
  shouldUpdate() {
    return true;
  }

  fetch() {
    const { view, direction, client } = this;
    if (!client) return;

    const views = ['', 'year', 'month', 'day'];
    const aggregationType = ['YEARS', 'MONTHS', 'DAYS'];

    this.variables = {
      direction,
      deviceId: this.deviceId,

      allocationBody: (data) => {
        const { device } = data.exportVariables;
        let startDate;
        let endDate;


        if (view === 0) {
          startDate = moment(device.startDate).format('YYYY-MM-DD');
          endDate = device.endDate ? moment(device.endDate).format('YYYY-MM-DD') : moment().format('YYYY-MM-DD');
        } else {
          const date = this.date || device.endDate || moment().subtract(1, 'days').format('YYYY-MM-DD');
          startDate = moment(date, 'YYYY-MM-DD').startOf(views[view]).format('YYYY-MM-DD');
          endDate = moment(date, 'YYYY-MM-DD').endOf(views[view]).format('YYYY-MM-DD');
        }

        return {
          startDate,
          endDate,
          deviceId: this.deviceId,
          direction: this.direction.toUpperCase(),
          aggregationType: aggregationType[this.view],
        };
      },
    };
    this.query = query;
    this.requestUpdate();
  }

  get renderLoader() {
    return html`
      loading...
    `;
  }

  changeView(view, newDate) {
    const { date, min, max } = this;
    const views = ['year', 'month', 'day'];
    let currentMoment = newDate ? moment(newDate, 'YYYY-MM-DD') : moment(date, 'YYYY-MM-DD');

    if (view < 0 && view > 2) return;
    if (currentMoment.isBefore(moment(min, 'YYYY-MM-DD').startOf(views[view]))) {
      if (newDate) return;
      currentMoment = moment(min, 'YYYY-MM-DD');
    }

    if (currentMoment.isAfter(moment(max, 'YYYY-MM-DD').endOf(views[view]))) {
      if (newDate) return;
      currentMoment = moment(max, 'YYYY-MM-DD');
    }

    this.view = view;
    this.date = currentMoment.format('YYYY-MM-DD');
    this.fetch();
  }

  get renderCalendar() {
    const { view, date } = this;
    const views = ['', 'YYYY', 'MMMM YYYY', 'D MMMM'];
    const next = ['', 'years', 'months', 'days'];
    const currentMoment = date && moment(date, 'YYYY-MM-DD');
    const nextMoment = moment(currentMoment).subtract(1, next[view]).format('YYYY-MM-DD');
    const prevMoment = moment(currentMoment).add(1, next[view]).format('YYYY-MM-DD');

    if (view === 0) return null;

    return html`
      <ul class="calendar">
        <li @click=${() => { this.changeView(view, nextMoment); }}></li>
        <li>${currentMoment && currentMoment.format(views[view])}</li>
        <li @click=${() => { this.changeView(view, prevMoment); }}></li>
      </ul>
    `;
  }

  get renderView() {
    const views = ['years', 'months', 'days'];
    const items = views.map((v, i) => (
      html`<li class=${i === this.view && 'active'} @click=${() => { this.changeView(i); }}>${v}</li>`
    ));

    return html`
      <ul class="view">${items}</ul>
    `;
  }

  get renderTotal() {
    const total = this.data && this.data.response.allocation.content.records.totalValue;
    return html`
      <div class="total"><strong>Total ${total || 0}</strong> kWh</div>
    `;
  }

  get renderGraph() {
    if (!this.data) return false;

    const { view, date } = this;
    const names = ['YYYY', 'MMMM', 'D'];
    const graphData = this.data.response.allocation.content.records.periods.map((p) => {
      const values = {};
      const views = ['year', 'month', 'date'];
      const next = moment(p.period).get(views[view]);
      const newDate = moment(date, 'YYYY-MM-DD').set(views[view], next).format('YYYY-MM-DD');

      p.types.forEach((t) => {
        values[t.energyType.toLowerCase()] = t.value;
      });

      return {
        onClick: () => { this.changeView(view + 1, newDate); },
        name: moment(p.period).format(names[view]),
        values,
      };
    });
    const graphSeries = [
      {
        name: 'sun',
        label: 'Sun',
      },
      {
        name: 'wind',
        label: 'Wind',
      },
      {
        name: 'water',
        label: 'Water',
      },
    ];

    return html`
      <stacked-column-chart
        .data=${graphData}
        .series=${graphSeries}
      ></stacked-column-chart>
    `;
  }

  render() {
    const { loading, error } = this;
    const fetchError = error
      ? {
        title: 'Something went wrong.',
        message: error,
        retryTitle: 'Try again',
        retryAction: () => {
          this.error = null;
          this.query = query;
        },
      }
      : null;

    return html`
      <widget-wrapper .error=${fetchError}>
        <div class="flex-wrapper">
          <div class="tools">
            ${this.renderView}
            ${this.renderCalendar}
          </div>
          ${this.renderTotal}
          ${loading ? this.renderLoader : this.renderGraph}
        </div>
      </widget-wrapper>
    `;
  }
}

customElements.define('allocation-graph', AllocationGraph);
