/*
 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 { Fragment, h, VNode } from "preact";
import { Tooltip } from "../Tooltip.js";
import { InputProps, useField } from "./useField.js";
import { useRef } from "preact/hooks";
import { doAutoFocus } from "./Input.js";
import { useEffect } from "preact/hooks";

interface Props<T> extends InputProps<T> {
  inputExtra?: any;
  focus?: boolean;
  size: number;
  dashesIndex?: number[];
  filter: (c: string) => string | undefined;
  disabled?: boolean;
}

export function InputCode<T>({
  name,
  readonly,
  tooltip,
  label,
  help,
  focus,
  disabled,
  inputExtra,
  size,
  dashesIndex = [],
  filter,
}: Props<keyof T>): VNode {
  const { error, value, onChange, required } = useField<T>(name);

  const elementArray = Array.from<HTMLInputElement | null>({
    length: size,
  }).fill(null);

  const elements = useRef(elementArray);
  function result() {
    return elements.current.reduce((prev, cur) => {
      if (!cur?.value) return prev;
      const v = filter(cur.value);
      if (!v) return prev;
      return prev + v;
    }, "");
  }
  function checkAutoFocus() {
    // wait until all ref loaded
    const allRefHasLoaded = elements.current.every((e) => e !== null);
    if (!allRefHasLoaded) return;
    // only when this field doesn't have values
    if (result().length > 0) return;
    // focus on the first
    doAutoFocus(elements.current[0]);
  }

  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">
          <fieldset>
            {elementArray.map((v, idx) => {
              const strValue = value as string;
              const defValue =
                !strValue || strValue.length <= idx
                  ? undefined
                  : filter(strValue[idx]);

              const prevHaveDash = dashesIndex.indexOf(idx - 1) !== -1;
              const haveDash = dashesIndex.indexOf(idx) !== -1;
              return (
                <Fragment>
                  <input
                    id={String(idx)}
                    {...(inputExtra as any)}
                    // ref={focus && idx === 0 ? doAutoFocus : undefined}
                    ref={(el) => {
                      elements.current[idx] = el;
                      if (focus) checkAutoFocus();
                    }}
                    class={
                      error ? "input is-danger mfa-code" : "input mfa-code"
                    }
                    type={"number"}
                    inputMode={"numeric"}
                    placeholder={"\u00B7"}
                    onPaste={(e) => {
                      e.preventDefault();
                      const content = e.clipboardData?.getData("text");
                      if (content) {
                        const cleared = content.replace(/[ -]/g, "");
                        if (cleared.match(/^[0-9]{8}$/)) {
                          elements.current
                            .reduce((prev, cur, idx): Promise<void> => {
                              if (!cur) return prev;
                              return prev.then((d) => {
                                return new Promise((res, rej) => {
                                  setTimeout(() => {
                                    cur.value = cleared[idx];
                                    res(undefined);
                                  }, 60);
                                });
                              });
                            }, Promise.resolve())
                            .then((r) => {
                              onChange(cleared as any);
                            });
                        }
                      }
                    }}
                    cols={1}
                    defaultValue={defValue}
                    style={{
                      padding: 1,
                      width: 26,
                      textAlign: "center",
                      borderLeft: idx === 0 || prevHaveDash ? undefined : "0px",
                      borderTopLeftRadius:
                        idx === 0 || prevHaveDash ? undefined : "0px",
                      borderBottomLeftRadius:
                        idx === 0 || prevHaveDash ? undefined : "0px",

                      borderRight:
                        idx === size - 1 || haveDash ? undefined : "0px",
                      borderTopRightRadius:
                        idx === size - 1 || haveDash ? undefined : "0px",
                      borderBottomRightRadius:
                        idx === size - 1 || haveDash ? undefined : "0px",
                    }}
                    readonly={readonly}
                    disabled={readonly}
                    autoComplete={"off"}
                    name={String(name)}
                    onKeyDown={(e) => {
                      if (e.key === "Backspace") {
                        if (idx > 0) {
                          elements.current[idx - 1]?.focus();
                          e.currentTarget.value = ""
                          onChange(undefined as any)
                          e.preventDefault();
                        }
                      } else if (e.key === "ArrowLeft") {
                        if (idx > 0) {
                          elements.current[idx - 1]?.focus();
                          e.preventDefault();
                        }
                      } else if (e.key === "ArrowRight") {
                        if (idx < elements.current.length) {
                          elements.current[idx + 1]?.focus();
                          e.preventDefault();
                        }
                      }
                    }}
                    onFocus={(e) => {
                      e.preventDefault();
                      e.currentTarget.select();
                    }}
                    onChange={(e) => {
                      e.preventDefault();
                      const v = filter(e.currentTarget.value);
                      e.currentTarget.value = v ?? "";
                      if (v === undefined) {
                        onChange(undefined as any);
                        return;
                      }

                      if (idx < elements.current.length) {
                        elements.current[idx + 1]?.focus();
                      }

                      const total = result();
                      if (total.length === size) {
                        onChange(total as any);
                      }
                    }}
                  />
                  {haveDash ? (
                    <input
                      class="input mfa-code"
                      defaultValue={"-"}
                      tabIndex={-1}
                      // disabled
                      readOnly
                      style={{
                        border: 0,
                        padding: 1,
                        width: 26,
                        textAlign: "center",
                      }}
                    />
                  ) : undefined}
                </Fragment>
              );
            })}
          </fieldset>
          {help}
          {error && (
            <p class="help is-danger" style={{ fontSize: 16 }}>
              {error}
            </p>
          )}
        </div>
      </div>
    </div>
  );
}
