/*
 This file is part of GNU Taler
 (C) 2021-2024 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 Sebastian Javier Marchano (sebasjm)
 */
import {
  assertUnreachable,
  Duration,
  InternationalizationAPI,
  TranslatedString,
} from "@gnu-taler/taler-util";
import { useTranslationContext } from "@gnu-taler/web-util/browser";
import { ComponentChildren, h, VNode } from "preact";
import { useEffect, useState } from "preact/hooks";
import { Tooltip } from "../Tooltip.js";
import { InternalTextInputSwitch } from "./Input.js";
import { InputProps, useField } from "./useField.js";

const TALER_SCREEN_ID = 8;

export interface Props<T> extends InputProps<T> {
  expand?: boolean;
  readonly?: boolean;
  withForever?: boolean;
  side?: ComponentChildren;
  withoutClear?: boolean;
  focus?: boolean;
  useProtocolDuration?: boolean;
}

function defaultToInputNumberString(
  d: Duration | undefined,
  unit: PosibleUnits | undefined,
): string | undefined {
  if (!d) return "";
  if (d.d_ms === "forever") return "Forever";
  switch (unit) {
    case undefined:
      return "";
    case "days":
      return String(Math.trunc(d.d_ms / (1000 * 60 * 60 * 24)));
    case "hours":
      return String(Math.trunc(d.d_ms / (1000 * 60 * 60)));
    case "minutes":
      return String(Math.trunc(d.d_ms / (1000 * 60)));
    case "seconds":
      return String(Math.trunc(d.d_ms / 1000));
    case "forever":
      return "";
    default:
      assertUnreachable(unit);
  }
}
function defaultFromInputToDuration(
  value: string | undefined,
  unit: PosibleUnits | undefined,
): Duration | undefined {
  if (!value) return undefined;
  const num = Number.parseInt(value, 10);
  if (Number.isNaN(num)) return undefined;

  switch (unit) {
    case undefined:
      return undefined;
    case "days":
      return Duration.fromSpec({ days: num });
    case "hours":
      return Duration.fromSpec({ hours: num });
    case "minutes":
      return Duration.fromSpec({ minutes: num });
    case "seconds":
      return Duration.fromSpec({ seconds: num });
    case "forever":
      return { d_ms: "forever" };
    default:
      assertUnreachable(unit);
  }
}

const UNIT_VALUES = ["forever", "days", "hours", "minutes", "seconds"] as const;
type PosibleUnits = (typeof UNIT_VALUES)[number];

function isDivisible(v: number, size: number) {
  return v / size - Math.trunc(v / size) === 0;
}

function bestInitialUnit(v: Duration | undefined): PosibleUnits | undefined {
  if (v === undefined) {
    return undefined;
  }
  if (v.d_ms === "forever") {
    return "forever";
  }
  if (isDivisible(v.d_ms, 1000 * 60 * 60 * 24)) {
    return "days";
  }
  if (isDivisible(v.d_ms, 1000 * 60 * 60)) {
    return "hours";
  }
  if (isDivisible(v.d_ms, 1000 * 60)) {
    return "minutes";
  }
  if (isDivisible(v.d_ms, 1000)) {
    return "seconds";
  }
  return "seconds";
}

function translateUnits(
  unit: PosibleUnits | undefined,
  i18n: InternationalizationAPI,
): TranslatedString {
  switch (unit) {
    case undefined:
      return i18n.str`Select one...`;
    case "days":
      return i18n.str`Days`;
    case "hours":
      return i18n.str`Hours`;
    case "minutes":
      return i18n.str`Minutes`;
    case "seconds":
      return i18n.str`Seconds`;
    case "forever":
      return i18n.str`Forever`;
    default:
      assertUnreachable(unit);
  }
}

export function InputDurationDropdown<T>({
  name,
  expand,
  placeholder,
  tooltip,
  label,
  help,
  readonly,
  withForever,
  withoutClear,
  focus,
  side,
  useProtocolDuration,
}: Props<keyof T>): VNode {
  const { i18n } = useTranslationContext();
  const { error, required, value: anyValue, onChange } = useField<T>(name);
  const value: Duration | undefined =
    anyValue && anyValue.d_us !== undefined
      ? Duration.fromTalerProtocolDuration(anyValue)
      : anyValue;

  const initialUnit = bestInitialUnit(value);

  const [unit, setUnit] = useState<PosibleUnits | undefined>(initialUnit);
  const [numStr, setNumStr] = useState<string | undefined>(
    defaultToInputNumberString(value, unit),
  );

  useEffect(() => {
    if (value === undefined && !numStr) return
    const nu = bestInitialUnit(value)
    setUnit(nu)
    setNumStr(defaultToInputNumberString(value, nu))
  },[value])
  // const strValue = durationToString(i18n, value);

  return (
    <div class="field is-horizontal">
      <div class="field-label is-normal">
        <label class="label">
          {label}
          {required && (
            <span class="has-text-danger" style={{ marginLeft: 5 }}>
              *
            </span>
          )}
          {tooltip && (
            <Tooltip text={tooltip}>
              <i class="icon mdi mdi-information" />
            </Tooltip>
          )}
        </label>
      </div>

      <div class="field-body is-flex-grow-3">
        <div class="field" style={{ flexGrow: "initial" }}>
          <InternalTextInputSwitch
            class={error ? "input is-danger" : "input"}
            label={undefined}
            inputType={"numeric"}
            placeholder={placeholder}
            min={0}
            cols={2}
            readonly={readonly}
            style={{
              textAlign: "right",
              width: "6em",
            }}
            fromStr={(v) => (!v ? undefined : parseInt(v, 10))}
            toStr={(v) => `${v}`}
            disabled={readonly || unit === "forever"}
            focus={focus}
            name={String(name)}
            value={numStr}
            onChange={(e: h.JSX.TargetedEvent<HTMLInputElement>): void => {
              const v = e.currentTarget.value;
              setNumStr(v);
              const duration = defaultFromInputToDuration(v, unit);
              onChange(
                (useProtocolDuration && duration
                  ? Duration.toTalerProtocolDuration(duration)
                  : duration) as any,
              );
            }}
          />
          {/* </div>
        <div class="field"> */}
          <p class={expand ? "control is-expanded select" : "control select "}>
            <select
              class={error ? "select is-danger" : "select"}
              name={String(name)}
              disabled={readonly}
              readonly={readonly}
              onChange={(e) => {
                const u = e.currentTarget.value as PosibleUnits;
                setUnit(u);
                const duration = defaultFromInputToDuration(numStr, u);
                if (!duration || duration.d_ms === "forever") {
                  setNumStr("")
                }
                onChange(
                  (useProtocolDuration && duration
                    ? Duration.toTalerProtocolDuration(duration)
                    : duration) as any,
                );
              }}
            >
              {placeholder && <option>{placeholder}</option>}
              {UNIT_VALUES.map((v, i) => {
                if (!withForever && v === "forever") {
                  return undefined;
                }
                return (
                  <option key={i} value={v} selected={unit === v}>
                    {translateUnits(v, i18n)}
                  </option>
                );
              })}
              <option key="unslected" value="" selected={unit === undefined}>
                {translateUnits(undefined, i18n)}
              </option>
            </select>
          </p>

          <div>
            <span class="has-text-grey">{help}</span>
            {error && (
              <p class="help is-danger" style={{ fontSize: 16 }}>
                {error}
              </p>
            )}
          </div>
        </div>

        {withForever && (
          <Tooltip text={i18n.str`Change the value to never`}>
            <button
              type="button"
              class="button is-info mr-3"
              onClick={() => {
                if (useProtocolDuration) {
                  onChange({ d_us: "forever" } as any);
                } else {
                  onChange({ d_ms: "forever" } as any);
                }
              }}
            >
              <i18n.Translate>Forever</i18n.Translate>
            </button>
          </Tooltip>
        )}
        {!readonly && !withoutClear && (
          <Tooltip text={i18n.str`Change the value to empty`}>
            <button
              type="button"
              class="button is-info mr-3"
              onClick={() => onChange(undefined as any)}
            >
              <i18n.Translate>Clear</i18n.Translate>
            </button>
          </Tooltip>
        )}
        {side}
      </div>
    </div>
  );
}
