/*
   This file is part of GNU Taler
   (C) 2021-2025 Taler Systems S.A.

   GNU Taler is free software; you can redistribute it and/or modify it under the
   terms of the GNU General Public License as published by the Free Software
   Foundation; either version 3, or (at your option) any later version.

   GNU Taler is distributed in the hope that it will be useful, but WITHOUT ANY
   WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
   A PARTICULAR PURPOSE.  See the GNU General Public License for more details.

   You should have received a copy of the GNU General Public License along with
   GNU Taler; see the file COPYING.  If not, see <http://www.gnu.org/licenses/>
   */

/**
 *
 * @author Martin Schanzenbach
 */

import {
  AbsoluteTime,
  Amounts,
  HttpStatusCode,
  StatisticsAmount,
  TalerError,
  assertUnreachable,
  StatisticsCounter,
  AmountJson,
  StatisticBucketRange,
} from "@gnu-taler/taler-util";
import { useTranslationContext } from "@gnu-taler/web-util/browser";
import { VNode, h } from "preact";
import { useState } from "preact/hooks";
import { ErrorLoadingMerchant } from "../../../../components/ErrorLoadingMerchant.js";
import { Loading } from "../../../../components/exception/loading.js";
import { NotificationCard } from "../../../../components/menu/index.js";
import { Notification } from "../../../../utils/types.js";
import { LoginPage } from "../../../login/index.js";

import {
  Chart as ChartJS,
  CategoryScale,
  LinearScale,
  BarElement,
  PointElement,
  LineElement,
  ChartDataset,
  Title,
  TimeScale,
  Tooltip,
  Legend,
  Filler,
  Point,
  ChartOptions,
  LineOptions,
  ComplexFillTarget,
} from 'chart.js';
import 'chartjs-adapter-date-fns';
import { RevenueChart, RevenueChartFilter } from "./RevenueChart.js";
import { OrdersChart } from "./OrdersChart.js";
import { useInstanceStatisticsAmount, useInstanceStatisticsCounter } from "../../../../hooks/statistics.js";
import { sub } from "date-fns";
import { addTestValues } from "./testing.js";
interface Props {
}

ChartJS.register(
  CategoryScale,
  LinearScale,
  TimeScale,
  LineElement,
  PointElement,
  BarElement,
  Title,
  Filler,
  Tooltip,
  Legend
);

export interface StatSlug {
  slug: string,
  text: string
}

const chartColors = new Map<string, string>();
  chartColors.set("orders-created", '#b9a5ff');
  chartColors.set("tokens-issued", '#b9a5ff');
  chartColors.set("payments-received-after-deposit-fee", '#b9a5ff');
  chartColors.set("orders-claimed", '#647cda');
  chartColors.set("total-wire-fees-paid", '#647cda');
  chartColors.set("total-deposit-fees-paid", '#2830a8');
  chartColors.set("orders-settled", '#2830a8');
  chartColors.set("tokens-used", '#2830a8');
  chartColors.set("orders-paid", '#525597');
  chartColors.set("refunds-granted", '#525597');

export default function Statistics({
}: Props): VNode {
  const [notif, _] = useState<Notification | undefined>(undefined);
  const ordersStats = new Map<string, StatisticsCounter>();
  const revenueStats = new Map<string, StatisticsAmount>();

  const currentDate = new Date();
  const lastMonthDate = sub(currentDate, { months: 1 });
  const lastMonthAbs: AbsoluteTime = {t_ms: lastMonthDate.getMilliseconds() } as AbsoluteTime;
  const [startOrdersFromDate, setStartOrdersFromDate] = useState<AbsoluteTime | undefined>(lastMonthAbs);
  const [revenueChartFilter, setRevenueChartFilter] = useState<RevenueChartFilter>({
    range: StatisticBucketRange.Quarter,
    rangeCount: 4,
    currency: "", // FIXME get this from merchant?
  });

  for (let ordersSlug of ["orders-created", "orders-claimed", "orders-paid", "orders-settled"]) {
    let res = useInstanceStatisticsCounter(ordersSlug);
    if (!res) return <Loading />;
    if (res instanceof TalerError) {
      return <ErrorLoadingMerchant error={res} />;
    }
    if (res.type === "fail") {
      switch (res.case) {
        case HttpStatusCode.Unauthorized: {
          return <LoginPage />;
        }
        case HttpStatusCode.BadGateway: {
          return <div />;
        }
        case HttpStatusCode.ServiceUnavailable: {
          return <div />;
        }
        case HttpStatusCode.Unauthorized: {
          return <div />;
        }
        case HttpStatusCode.NotFound: {
          return <div />;
        }
         default: {
          assertUnreachable(res);
        }
      }
    }
    ordersStats.set(ordersSlug, res.body);
  }
  for (let revenueSlug of ["payments-received-after-deposit-fee",
                           "total-wire-fees-paid",
                           "refunds-granted",
                           "total-deposit-fees-paid"]) {
                             let res = useInstanceStatisticsAmount(revenueSlug);
    if (!res) return <Loading />;
    if (res instanceof TalerError) {
      return <ErrorLoadingMerchant error={res} />;
    }
    if (res.type === "fail") {
      switch (res.case) {
        case HttpStatusCode.Unauthorized: {
          return <LoginPage />;
        }
        case HttpStatusCode.BadGateway: {
          return <div />;
        }
        case HttpStatusCode.ServiceUnavailable: {
          return <div />;
        }
        case HttpStatusCode.Unauthorized: {
          return <div />;
        }
        case HttpStatusCode.NotFound: {
          return <div />;
        }
         default: {
          assertUnreachable(res);
        }
      }
    }
    revenueStats.set(revenueSlug, res.body);
  }
  //if (true) {
  //  addTestValues(ordersStats, revenueStats);
  //  console.log(revenueStats);
  //}

  const orderStatsChartOptions = {
    plugins: {
      title: {
        display: true,
        text: 'Orders',
      },
      filler: {
        propagate: true
      }
    },
    responsive: true,
    scales: {
      x: {
        min: startOrdersFromDate?.t_ms,
        type: "time",
        time: { unit: "day", displayFormats: { day: "Pp" }},
        ticks: { source: "data"},
        stacked: true,
        title: { display: false }
      },
      y: {
        stacked: true,
        title: { display: true, text: "# of orders since", align: "end" }
      },
    },
  };
  const revenueChartOptions = {
    plugins: {
      title: {
        display: true,
        text: 'Revenue',
      },
    },
    responsive: true,
    scales: {
      x: {
        type: "time",
        time: { unit: "quarter" },
        ticks: { source: "labels"},
        stacked: true,
        title: { display: true, text: "Time range", align: "end" }
      },
      y: {
        stacked: true,
        title: { display: true, text: "Euro (€)", align: "end" }
      },
    },
  };
  var revenueChartDatasets = new Map<string, ChartDataset[]>();
  var orderStatsChartDatasets: ChartDataset[] = [];

  const revenueChartLabels = new Map<string, number[]>();
  const revenueCurrencies: string[] = [];
  revenueStats.forEach((stat: StatisticsAmount, slug: string) => {
    var datasetForCurrency = new Map<string, ChartDataset>();
    for (let b of stat.buckets) {
      // We collect all currencies that we have stats of before we
      // filter.
      // FIXME kind of ugly.
      // We probably want to build all datasets here and filter later.
      b.cumulative_amounts.map ((c) => {
        const a = Amounts.parse(c);
        if (a && revenueCurrencies.indexOf(a.currency) === -1) {
          revenueCurrencies.push(a.currency);
        }
      });
      if (b.start_time.t_s == "never") {
        continue;
      }

      for (let c of b.cumulative_amounts) {
        const a = Amounts.parse(c);
        if (!a) {
          continue;
        }
        // If unset, set to the first currency we find.
        if ("" === revenueChartFilter.currency) {
          revenueChartFilter.currency = a.currency;
        }
        if (revenueChartFilter.range !== b.range) {
          continue;
        }
        const datasetColor = chartColors.get(slug ?? "") ?? "#eeeeee";
        if (!datasetForCurrency.has(a.currency)) {
          datasetForCurrency.set(a.currency, {
            label: stat.buckets_description,
            data: Array<Point & AmountJson>(),
            backgroundColor: datasetColor,
            hoverOffset: 4,
          })
        }
        const amount = a.value + (a.fraction / Math.pow(10, 8));
        const timeRangeStart = b.start_time.t_s * 1000;
        datasetForCurrency.get(a.currency)!.data.push({x: timeRangeStart, y: amount, ...a});
        if (revenueChartFilter.rangeCount < datasetForCurrency.get(a.currency)!.data.length) {
          datasetForCurrency.get(a.currency)?.data.shift(); // Remove oldest entry
          revenueChartLabels.get(a.currency)?.shift(); // Remove oldest entry
        }
        if (!revenueChartLabels.has(a.currency)) {
          revenueChartLabels.set(a.currency, []);
        }
        const labels = revenueChartLabels.get(a.currency);
        if (labels?.indexOf(timeRangeStart) === -1) {
          labels?.push(timeRangeStart);
        }
      }
    }
    datasetForCurrency.forEach((value: ChartDataset, key: string) => {
      if (!revenueChartDatasets.has(key)) {
        revenueChartDatasets.set(key, []);
      }
      revenueChartDatasets.get(key)?.push(value);
    })
  });
  var smallestOrderDate: number = -1;
  const orderStatsChartLabels: number[] = [];
  ordersStats.forEach((stat: StatisticsCounter, slug: string) => {
    const datasetColor = chartColors.get(slug ?? "") ?? "#eeeeee";
    const dataset: ChartDataset = {
      label: stat.intervals_description,
      data: Array<Point>(),
      backgroundColor:  datasetColor,
      hoverOffset: 4,
      fill: {
        target: "-1",
        below: datasetColor,
      }
    }
    var accum = 0;
    for (let j = stat.intervals.length - 1; j >= 0; j--) {
      const interval = stat.intervals[j];
      if (interval.start_time.t_s == "never") {
        continue;
      }
      accum += interval.cumulative_counter;
      dataset.data.push({x: interval.start_time.t_s * 1000, y: accum});
      // Do not add label if outside of range
      const intervalStart = interval.start_time.t_s * 1000;
      if ((startOrdersFromDate) &&
          ("never" !== startOrdersFromDate.t_ms) &&
          (intervalStart < startOrdersFromDate.t_ms)) {
          continue;
      }
      if (-1 === orderStatsChartLabels.indexOf(intervalStart)) {
        orderStatsChartLabels.push(intervalStart);
      }
      if (smallestOrderDate < 0) {
        smallestOrderDate = intervalStart;
      }
      if (smallestOrderDate > intervalStart) {
        smallestOrderDate = intervalStart;
      }
    }
    orderStatsChartDatasets.push(dataset);
  });
  orderStatsChartLabels.sort();
  // Hack to make first area to origin work.
  const ldo = orderStatsChartDatasets[orderStatsChartDatasets.length - 1] as LineOptions;
  (ldo.fill as ComplexFillTarget).target = "origin";
  // Set the orders from date to the smallest interval we found
  if (startOrdersFromDate && smallestOrderDate && (startOrdersFromDate.t_ms != "never") && (startOrdersFromDate.t_ms < smallestOrderDate)) {
    setStartOrdersFromDate({ t_ms: smallestOrderDate } as AbsoluteTime);
  }
  orderStatsChartDatasets = orderStatsChartDatasets.reverse();

  // FIXME: End date picker?
  // FIXME: Intelligent date range selection?
  revenueChartOptions.scales.y.title.text = revenueChartFilter.currency;
  revenueChartOptions.scales.x.time.unit = revenueChartFilter.range;
  const a: VNode = (
    <section class="section is-main-section">
    <NotificationCard notification={notif} />

    <div>
    <RevenueChart
        chartLabels={revenueChartLabels.get(revenueChartFilter.currency)?.sort().reverse() ?? []}
        chartData={revenueChartDatasets.get(revenueChartFilter.currency)}
        chartOptions={revenueChartOptions as ChartOptions}
        activeFilter={revenueChartFilter}
        availableCurrencies={revenueCurrencies}
        onUpdateFilter={(filter: RevenueChartFilter) => setRevenueChartFilter(filter)}
        />
    </div>
    <hr/>
    <OrdersChart
      chartLabels={orderStatsChartLabels.reverse()}
      chartData={orderStatsChartDatasets}
      chartOptions={orderStatsChartOptions}
      filterFromDate={startOrdersFromDate}
      onSelectDate={(d?: AbsoluteTime) => { setStartOrdersFromDate(d)}}
      />
    </section>
  );
  return a;
}





