/*
 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,
  HttpStatusCode,
  InternationalizationAPI,
  StatisticsCounter,
  TalerError,
} from "@gnu-taler/taler-util";
import { Loading, useTranslationContext } from "@gnu-taler/web-util/browser";
import { ChartDataset, ChartOptions, Point } from "chart.js";
import { format } from "date-fns";
import { Fragment, VNode, h } from "preact";
import { useState } from "preact/hooks";
import { DatePicker } from "../../../../components/picker/DatePicker.js";
import {
  dateFormatForSettings,
  usePreference,
} from "../../../../hooks/preference.js";
import {
  MerchantOrderStatsSlug,
  useInstanceStatisticsCounter,
} from "../../../../hooks/statistics.js";
import { ErrorLoadingMerchant } from "../../../../components/ErrorLoadingMerchant.js";
import { NotFoundPageOrAdminCreate } from "../../../notfound/index.js";
import { LoginPage } from "../../../login/index.js";
import { NotificationCard } from "../../../../components/menu/index.js";
import { LineCanvas } from "../../../../components/ChartJS.js";

const TALER_SCREEN_ID = 58;

export interface OrdersChartProps {
  colors: Map<MerchantOrderStatsSlug, string>;
  filterFromDate?: AbsoluteTime;
  onSelectDate: (date?: AbsoluteTime) => void;
}

function getCountForLabelAndDataset(
  label: number,
  ds: ChartDataset<"line", Point[]>,
): number {
  for (let d of ds.data) {
    if (d.x === label) {
      return d.y;
    }
  }
  return 0;
}

export function OrdersChart({
  colors,
  filterFromDate,
  onSelectDate,
}: OrdersChartProps): VNode {
  const { i18n } = useTranslationContext();
  const [settings] = usePreference();
  const [pickDate, setPickDate] = useState(false);
  const [showTable, setShowTable] = useState<boolean>(false);
  const counters = useInstanceStatisticsCounter();
  if (!counters) {
    return <Loading />;
  }
  if (counters instanceof TalerError) {
    return <ErrorLoadingMerchant error={counters} />;
  }
  if (counters.type === "fail") {
    switch (counters.case) {
      case HttpStatusCode.Unauthorized:
        return <LoginPage />;
      case HttpStatusCode.NotFound:
        return <NotFoundPageOrAdminCreate />;
      case HttpStatusCode.BadGateway:
        return (
          <NotificationCard
            notification={{
              message: i18n.str`Bad gateway`,
              type: "ERROR",
            }}
          />
        );

      case HttpStatusCode.ServiceUnavailable:
        return (
          <NotificationCard
            notification={{
              message: i18n.str`Service unavailable`,
              type: "ERROR",
            }}
          />
        );
    }
  }
  const chart = filterOrderCharData(colors, counters.body, filterFromDate);

  const dateTooltip = i18n.str`Select date from which to show statistics`;
  return (
    <Fragment>
      <div class="tabs" style={{ overflow: "inherit" }}>
        <ul>
          <li class={!showTable ? "is-active" : ""}>
            <div class="has-tooltip-right" data-tooltip={i18n.str`Show chart`}>
              <a
                onClick={() => {
                  setShowTable(false);
                }}
              >
                <i18n.Translate>Orders chart</i18n.Translate>
              </a>
            </div>
          </li>
          <li class={showTable ? "is-active" : ""}>
            <div class="has-tooltip-right" data-tooltip={i18n.str`Show table`}>
              <a
                onClick={() => {
                  setShowTable(true);
                }}
              >
                <i18n.Translate>Orders table</i18n.Translate>
              </a>
            </div>
          </li>
        </ul>
      </div>
      <div class="columns">
        <div class="column">
          <div class="buttons is-right">
            <div class="field has-addons">
              {filterFromDate && (
                <div class="control">
                  <a
                    class="button is-fullwidth"
                    onClick={() => onSelectDate(undefined)}
                  >
                    <span
                      class="icon"
                      data-tooltip={i18n.str`Clear date filter`}
                    >
                      <i class="mdi mdi-close" />
                    </span>
                  </a>
                </div>
              )}
              <div class="control">
                <span class="has-tooltip-top" data-tooltip={dateTooltip}>
                  <input
                    class="input"
                    type="text"
                    readonly
                    value={
                      !filterFromDate || filterFromDate.t_ms === "never"
                        ? ""
                        : format(
                            filterFromDate.t_ms,
                            dateFormatForSettings(settings),
                          )
                    }
                    placeholder={i18n.str`Start from (${dateFormatForSettings(
                      settings,
                    )})`}
                    onClick={() => {
                      setPickDate(true);
                    }}
                  />
                </span>
              </div>
              <div class="control">
                <span class="has-tooltip-left" data-tooltip={dateTooltip}>
                  <a
                    class="button  is-fullwidth"
                    onClick={() => {
                      setPickDate(true);
                    }}
                  >
                    <span class="icon">
                      <i class="mdi mdi-calendar" />
                    </span>
                  </a>
                </span>
              </div>
            </div>
          </div>
        </div>
      </div>

      <DatePicker
        opened={pickDate}
        closeFunction={() => setPickDate(false)}
        dateReceiver={(d) => {
          onSelectDate(AbsoluteTime.fromMilliseconds(d.getTime()));
        }}
      />
      <div class="card has-table">
        <header class="card-header">
          <p class="card-header-title">
            <span class="icon">
              <i class="mdi mdi-shopping" />
            </span>
            {i18n.str`Orders statistics`}
          </p>
        </header>
        <div class="card-content">
          {chart.datasets.length > 0 && chart.labels.length > 0 ? (
            !showTable ? (
              <LineCanvas
                data={chart}
                options={orderStatsChartOptions(i18n, chart.smallestOrderDate)}
              />
            ) : (
              <div class="b-table has-pagination">
                <div class="table-wrapper has-mobile-cards">
                  <div class="table-container">
                    <table class="table is-fullwidth is-striped is-hoverable is-fullwidth">
                      <thead>
                        <tr>
                          <th>
                            <i18n.Translate>Since</i18n.Translate>
                          </th>
                          {chart.datasets.map((d) => {
                            return (
                              <Fragment key={d.label}>
                                <th>{d.label}</th>
                              </Fragment>
                            );
                          })}
                        </tr>
                      </thead>
                      <tbody>
                        {chart.labels.map((l) => {
                          return (
                            <Fragment key={l}>
                              <tr key="info">
                                <td>{new Date(l).toLocaleString()}</td>
                                {chart.datasets.map((d) => {
                                  return (
                                    <Fragment key={d.label}>
                                      <td>
                                        {getCountForLabelAndDataset(l, d)}
                                      </td>
                                    </Fragment>
                                  );
                                })}
                              </tr>
                            </Fragment>
                          );
                        })}
                      </tbody>
                    </table>
                  </div>
                </div>
              </div>
            )
          ) : (
            <i>{i18n.str`No order statistics yet.`}</i>
          )}
        </div>
      </div>
    </Fragment>
  );
}

function orderStatsChartOptions(
  i18n: InternationalizationAPI,
  startOrdersFromDate: number | undefined,
): ChartOptions<"line"> {
  return {
    plugins: {
      title: {
        display: true,
        text: i18n.str`Orders`,
      },
      filler: {
        propagate: true,
      },
    },
    responsive: true,
    scales: {
      x: {
        min: startOrdersFromDate,
        type: "time",
        time: { unit: "day", displayFormats: { day: "Pp" } },
        ticks: { source: "data" },
        stacked: true,
        title: { display: false },
      },
      y: {
        stacked: true,
        title: {
          display: true,
          text: i18n.str`# of orders since`,
          align: "end",
        },
      },
    },
  };
}
function filterOrderCharData(
  colors: Map<MerchantOrderStatsSlug, string>,
  ordersStats: Map<MerchantOrderStatsSlug, StatisticsCounter>,
  startOrdersFromDate: AbsoluteTime | undefined,
): {
  datasets: ChartDataset<"line", Point[]>[];
  labels: number[];
  smallestOrderDate: number | undefined;
} {
  let smallestOrderDate: number | undefined;
  const labels: number[] = [];
  const datasets: ChartDataset<"line", Point[]>[] = [];
  ordersStats.forEach((stat, slug) => {
    const datasetColor = colors.get(slug) ?? "#eeeeee";
    const info: ChartDataset<"line", Point[]> = {
      label: stat.intervals_description,
      data: [],
      backgroundColor: datasetColor,
      // hoverOffset: 4,
      fill: {
        target: "-1",
        below: datasetColor,
      },
    };
    let 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;
      info.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 === labels.indexOf(intervalStart)) {
        labels.push(intervalStart);
      }
      if (!smallestOrderDate) {
        smallestOrderDate = intervalStart;
      }
      if (smallestOrderDate > intervalStart) {
        smallestOrderDate = intervalStart;
      }
    }
    if (info.data.length) {
      datasets.push(info);
    }
  });

  // Hack to make first area to origin work.
  if (
    datasets.length &&
    typeof datasets[0].fill === "object" &&
    "target" in datasets[0].fill
  ) {
    datasets[0].fill = "origin";
  }
  return { datasets, labels, smallestOrderDate };
}
