/*
 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 { ComponentChildren, h, VNode } from "preact";
import { InputProps, useField } from "./useField.js";
import { Tooltip } from "../Tooltip.js";

export type SupportedTextInputType =
  | "text"
  | "numeric"
  | "decimal"
  | "tel"
  | "email"
  | "url"
  | "search"
  | "multiline"
  | "password";

// https://developer.mozilla.org/en-US/docs/Web/HTML/Reference/Attributes/autocomplete
export type SupportedAutocomplete =
  | "username"
  | "current-password"
  | "new-password"
  | "one-time-code"
  | "organization"
  | "street-address"
  | "country"
  | "postal-code"
  | "language"
  | "url"
  | "photo"
  | "email"
  | "tel";

type CompatibleHTMLInput = Omit<
  Omit<
    Omit<
      Omit<h.JSX.HTMLAttributes<HTMLInputElement>, "readonly">,
      "placeholder"
    >,
    "name"
  >,
  "label"
>;
interface Props<T> extends InputProps<T>, CompatibleHTMLInput {
  inputType?: SupportedTextInputType;
  expand?: boolean;
  toStr?: (v?: any) => string;
  fromStr?: (s: string) => any;
  inputExtra?: any;
  side?: ComponentChildren;
  children?: ComponentChildren;
  focus?: boolean;
  dontRemember?: boolean;
  autoComplete?: SupportedAutocomplete;
}

export function doAutoFocus<T extends HTMLElement>(
  element: T | null | undefined,
) {
  if (element) {
    setTimeout(() => {
      element.focus({ preventScroll: true });
    }, 100);
  }
}

const defaultToString = (f?: any): string => f || "";
const defaultFromString = (v: string): any => v as any;

export function InternalTextInputSwitch({
  inputType,
  focus,
  hasError,
  ...rest
}: Props<any> & {
  value?: string;
  class?: string;
  disabled?: boolean;
  hasError?: boolean;
}): VNode {
  if (inputType === "multiline") {
    return (
      <textarea
        {...(rest as any)}
        ref={focus ? doAutoFocus : undefined}
        class={hasError ? "textarea is-danger" : "textarea"}
        rows={3}
      />
    );
  }
  if (inputType === "numeric" || inputType === "decimal") {
    return (
      <input
        {...(rest as any)}
        ref={focus ? doAutoFocus : undefined}
        class={hasError ? "input is-danger" : "input"}
        type={"number"}
        inputMode={inputType}
      />
    );
  }
  if (
    inputType === "email" ||
    inputType === "tel" ||
    inputType === "search" ||
    inputType === "url"
  ) {
    <input
      {...(rest as any)}
      ref={focus ? doAutoFocus : undefined}
      class={hasError ? "input is-danger" : "input"}
      type={inputType}
      inputMode={inputType}
    />;
  }
  return (
    <input
      {...(rest as any)}
      ref={focus ? doAutoFocus : undefined}
      class={hasError ? "input is-danger" : "input"}
      type={inputType ?? "text"}
    />
  );
}

export function Input<T>({
  name,
  readonly,
  placeholder,
  tooltip,
  label,
  expand,
  help,
  children,
  focus,
  dontRemember,
  inputType,
  inputExtra,
  autoComplete,
  side,
  fromStr = defaultFromString,
  toStr = defaultToString,
}: Props<keyof T>): VNode {
  const { error, value, onChange, required } = useField<T>(name);
  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">
          <p
            class={
              expand
                ? "control is-expanded has-icons-right"
                : "control has-icons-right"
            }
          >
            <fieldset>
              <InternalTextInputSwitch
                hasError={error}
                {...inputExtra}
                inputType={inputType}
                placeholder={placeholder}
                autocomplete={dontRemember ? "off" : undefined}
                readonly={readonly}
                disabled={readonly}
                autoComplete={autoComplete}
                name={String(name)}
                focus={focus}
                value={toStr(value)}
                onChange={(e: h.JSX.TargetedEvent<HTMLInputElement>): void =>
                  onChange(fromStr(e.currentTarget.value))
                }
              />
            </fieldset>
            {help}
            {children}
          </p>
          {error && (
            <p class="help is-danger" style={{ fontSize: 16 }}>
              {error}
            </p>
          )}
        </div>
        {side}
      </div>
    </div>
  );
}
