/*!
 * Matomo - free/libre analytics platform
 *
 * @link https://matomo.org
 * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
 */

import {
  reactive,
  computed,
  readonly,
} from 'vue';
import {
  AjaxHelper,
  translate,
  Site,
} from 'CoreHome';

interface SiteWithMetrics extends Site {
  label: string;
  nb_actions: string|number;
  nb_pageviews: string|number;
  nb_visits: string|number;
  pageviews_evolution: string;
  revenue: string;
  revenue_evolution: string;
  visits_evolution: string;
  ratio?: number|string;
  previous_nb_visits?: string|number;
  previous_Actions_nb_pageviews?: string|number;
  previous_Goal_revenue?: string|number;
  currencySymbol: string;
  periodName: string;
  previousRange: string;
  tooltip?: string;
}

interface SiteTotals {
  nb_actions: string|number;
  nb_pageviews: string|number;
  nb_visits: string|number;
  nb_visits_lastdate: string|number;
  revenue: string|number;
}

interface DashboardStoreState {
  sites: SiteWithMetrics[];
  isLoading: boolean;
  pageSize: number;
  currentPage: number;
  totalVisits: string|number;
  totalPageviews: string|number;
  totalActions: string|number;
  totalRevenue: string|number;
  searchTerm: string;
  lastVisits: string|number;
  lastVisitsDate: string;
  numberOfSites: number;
  loadingMessage: string;
  reverse: boolean;
  sortColumn: string;
  refreshInterval?: number;
  errorLoadingSites: boolean;
}

interface GetAllWithGroupsResponse {
  lastDate: string;
  numSites: number;
  sites: SiteWithMetrics[];
  totals: SiteTotals;
}

const { NumberFormatter } = window;

class DashboardStore {
  private privateState = reactive<DashboardStoreState>({
    sites: [],
    isLoading: false,
    pageSize: 25,
    currentPage: 0,
    totalVisits: '?',
    totalPageviews: '?',
    totalActions: '?',
    totalRevenue: '?',
    searchTerm: '',
    lastVisits: '?',
    lastVisitsDate: '?',
    numberOfSites: 0,
    loadingMessage: translate('MultiSites_LoadingWebsites'),
    reverse: true,
    sortColumn: 'nb_visits',
    refreshInterval: 0,
    errorLoadingSites: false,
  });

  private refreshTimeout: ReturnType<typeof setTimeout>|null = null;

  private fetchAbort: AbortController|null = null;

  readonly state = computed(() => readonly(this.privateState));

  readonly numberOfFilteredSites = computed(() => this.state.value.numberOfSites);

  readonly numberOfPages = computed(
    () => Math.ceil(this.numberOfFilteredSites.value / this.state.value.pageSize - 1),
  );

  readonly currentPagingOffset = computed(
    () => Math.ceil(this.state.value.currentPage * this.state.value.pageSize),
  );

  readonly paginationLowerBound = computed(() => this.currentPagingOffset.value + 1);

  readonly paginationUpperBound = computed(() => {
    let end = this.currentPagingOffset.value + this.state.value.pageSize;
    const max = this.numberOfFilteredSites.value;

    if (end > max) {
      end = max;
    }

    return end;
  });

  cancelRefereshInterval(): void {
    if (this.refreshTimeout) {
      clearTimeout(this.refreshTimeout);
      this.refreshTimeout = null;
    }
  }

  updateWebsitesList(report: GetAllWithGroupsResponse): void {
    if (!report) {
      this.onError();
      return;
    }

    const allSites = report.sites;
    allSites.forEach((site) => {
      if (site.ratio !== 1 && site.ratio !== '1') {
        const percent = NumberFormatter.formatPercent(
          Math.round(parseInt(site.ratio! as string, 10) * 100),
        );

        let metricName = null;
        let previousTotal = '0';
        let currentTotal = '0';
        let evolution = '0';
        let previousTotalAdjusted = '0';

        if (this.state.value.sortColumn === 'nb_visits'
          || this.state.value.sortColumn === 'visits_evolution'
        ) {
          previousTotal = NumberFormatter.formatNumber(site.previous_nb_visits);
          currentTotal = NumberFormatter.formatNumber(site.nb_visits);
          evolution = NumberFormatter.formatPercent(site.visits_evolution);
          metricName = translate('General_ColumnNbVisits');
          previousTotalAdjusted = NumberFormatter.formatNumber(
            Math.round(parseInt(site.previous_nb_visits as string, 10)
              * parseInt(site.ratio as string, 10)),
          );
        }

        if (this.state.value.sortColumn === 'pageviews_evolution') {
          previousTotal = `${site.previous_Actions_nb_pageviews}`;
          currentTotal = `${site.nb_pageviews}`;
          evolution = NumberFormatter.formatPercent(site.pageviews_evolution);
          metricName = translate('General_ColumnPageviews');
          previousTotalAdjusted = NumberFormatter.formatNumber(
            Math.round(parseInt(site.previous_Actions_nb_pageviews as string, 10)
              * parseInt(site.ratio as string, 10)),
          );
        }

        if (this.state.value.sortColumn === 'revenue_evolution') {
          previousTotal = NumberFormatter.formatCurrency(
            site.previous_Goal_revenue,
            site.currencySymbol,
          );
          currentTotal = NumberFormatter.formatCurrency(site.revenue, site.currencySymbol);
          evolution = NumberFormatter.formatPercent(site.revenue_evolution);
          metricName = translate('General_ColumnRevenue');
          previousTotalAdjusted = NumberFormatter.formatCurrency(
            Math.round(parseInt(site.previous_Goal_revenue as string, 10)
              * parseInt(site.ratio as string, 10)),
            site.currencySymbol,
          );
        }

        if (metricName) {
          site.tooltip = `${translate('MultiSites_EvolutionComparisonIncomplete', [percent])}\n`;
          site.tooltip += `${translate('MultiSites_EvolutionComparisonProportional', [
            percent,
            `${previousTotalAdjusted}`,
            metricName,
            `${previousTotal}`,
          ])}\n`;

          switch (site.periodName) {
            case 'day':
              site.tooltip += translate('MultiSites_EvolutionComparisonDay', [
                `${currentTotal}`,
                metricName,
                `${previousTotalAdjusted}`,
                site.previousRange,
                `${evolution}`,
              ]);
              break;

            case 'week':
              site.tooltip += translate('MultiSites_EvolutionComparisonWeek', [
                `${currentTotal}`,
                metricName,
                `${previousTotalAdjusted}`,
                site.previousRange,
                `${evolution}`,
              ]);
              break;

            case 'month':
              site.tooltip += translate('MultiSites_EvolutionComparisonMonth', [
                `${currentTotal}`,
                metricName,
                `${previousTotalAdjusted}`,
                site.previousRange,
                `${evolution}`,
              ]);
              break;

            case 'year':
              site.tooltip += translate('MultiSites_EvolutionComparisonYear', [
                `${currentTotal}`,
                metricName,
                `${previousTotalAdjusted}`,
                site.previousRange,
                `${evolution}`,
              ]);
              break;
            default:
              break;
          }
        }
      }
    });
    this.privateState.totalVisits = report.totals.nb_visits;
    this.privateState.totalPageviews = report.totals.nb_pageviews;
    this.privateState.totalActions = report.totals.nb_actions;
    this.privateState.totalRevenue = report.totals.revenue;
    this.privateState.lastVisits = report.totals.nb_visits_lastdate;
    this.privateState.sites = allSites;
    this.privateState.numberOfSites = report.numSites;
    this.privateState.lastVisitsDate = report.lastDate;
  }

  sortBy(metric: string): void {
    if (this.state.value.sortColumn === metric) {
      this.privateState.reverse = !this.state.value.reverse;
    }

    this.privateState.sortColumn = metric;
    this.fetchAllSites();
  }

  previousPage(): void {
    this.privateState.currentPage = this.state.value.currentPage - 1;
    this.fetchAllSites();
  }

  nextPage(): void {
    this.privateState.currentPage = this.state.value.currentPage + 1;
    this.fetchAllSites();
  }

  searchSite(term: string): void {
    this.privateState.searchTerm = term;
    this.privateState.currentPage = 0;
    this.fetchAllSites();
  }

  fetchAllSites(): Promise<void> {
    if (this.fetchAbort) {
      this.fetchAbort.abort();
      this.fetchAbort = null;

      this.cancelRefereshInterval();
    }

    this.privateState.isLoading = true;
    this.privateState.errorLoadingSites = false;
    const params: QueryParameters = {
      method: 'MultiSites.getAllWithGroups',
      hideMetricsDoc: '1',
      filter_sort_order: 'asc',
      filter_limit: this.state.value.pageSize,
      filter_offset: this.currentPagingOffset.value,
      showColumns: [
        'label',
        'nb_visits',
        'nb_pageviews',
        'visits_evolution',
        'visits_evolution_trend',
        'pageviews_evolution',
        'pageviews_evolution_trend',
        'revenue_evolution',
        'revenue_evolution_trend',
        'nb_actions,revenue',
      ].join(','),
    };

    if (this.privateState.searchTerm) {
      params.pattern = this.privateState.searchTerm;
    }

    if (this.privateState.sortColumn) {
      params.filter_sort_column = this.privateState.sortColumn;
    }

    if (this.privateState.reverse) {
      params.filter_sort_order = 'desc';
    }

    this.fetchAbort = new AbortController();
    return AjaxHelper.fetch<GetAllWithGroupsResponse>(
      params,
      { abortController: this.fetchAbort },
    ).then((response) => {
      this.updateWebsitesList(response);
    }).catch(() => {
      this.onError();
    }).finally(() => {
      this.privateState.isLoading = false;
      this.fetchAbort = null;

      if (this.state.value.refreshInterval && this.state.value.refreshInterval > 0) {
        this.cancelRefereshInterval();
        this.refreshTimeout = setTimeout(() => {
          this.refreshTimeout = null;
          this.fetchAllSites();
        }, this.state.value.refreshInterval! * 1000);
      }
    });
  }

  private onError(): void {
    this.privateState.errorLoadingSites = true;
    this.privateState.sites = [];
  }

  setRefreshInterval(interval?: number): void {
    this.privateState.refreshInterval = interval;
  }

  setPageSize(pageSize: number): void {
    this.privateState.pageSize = pageSize;
  }
}

export default new DashboardStore();
