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

 GNU Taler is free software; you can redistribute it and/or modify it under the
 terms of the GNU Affero 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 Affero Public License for more details.

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

import { AbsoluteTime } from "@gnu-taler/taler-util";
import { format, parse, parseISO } from "date-fns";
import { Fragment, VNode, h } from "preact";
import { useEffect, useState } from "preact/hooks";
import { Calendar } from "../Calendar.js";
import { Dialog } from "../Dialog.js";
import { UIFormProps } from "../FormProvider.js";
import { noHandlerPropsAndNoContextForField } from "./InputArray.js";
import { InputLine } from "./InputLine.js";

export interface InputIsoDateProps {
  /**
   * Pattern for displaying / parsing the date in the UI.
   *
   * Defaults to "dd/MM/yyyy".
   */
  pattern?: string;

  defaultValue?: string;

  /**
   * Default value when the calener widget is opened.
   */
  calendarDefaultValue?: string;
}

/**
 * Input field for an ISO date (yyyy-MM-dd).
 *
 * The user can enter the date in a format specified by a
 * pattern.
 */
export function InputIsoDate(
  properties: InputIsoDateProps & UIFormProps<string>,
): VNode {
  const pattern = properties.pattern ?? "dd/MM/yyyy";
  const [open, setOpen] = useState(false);

  const { value, onChange } =
    properties.handler ?? noHandlerPropsAndNoContextForField(properties.name);

  useEffect(() => {
    if (!value && !!properties.defaultValue) {
      onChange(properties.defaultValue);
    }
  }, [value, properties.handler, properties.defaultValue]);

  let calendarOpenTime: number;

  if (!value) {
    if (properties.calendarDefaultValue) {
      calendarOpenTime = parseISO(properties.calendarDefaultValue).getTime();
    } else {
      calendarOpenTime = Date.now();
    }
  } else {
    calendarOpenTime = parseISO(value).getTime();
  }
  return (
    <Fragment>
      <InputLine
        type="text"
        {...properties}
        after={{
          type: "button",
          onClick: () => {
            setOpen(true);
          },
          children: (
            <svg
              xmlns="http://www.w3.org/2000/svg"
              fill="none"
              viewBox="0 0 24 24"
              stroke-width="1.5"
              stroke="currentColor"
              class="w-6 h-6"
            >
              <path
                stroke-linecap="round"
                stroke-linejoin="round"
                d="M6.75 3v2.25M17.25 3v2.25M3 18.75V7.5a2.25 2.25 0 012.25-2.25h13.5A2.25 2.25 0 0121 7.5v11.25m-18 0A2.25 2.25 0 005.25 21h13.5A2.25 2.25 0 0021 18.75m-18 0v-7.5A2.25 2.25 0 015.25 9h13.5A2.25 2.25 0 0121 11.25v7.5"
              />
            </svg>
          ),
        }}
        converter={{
          toStringUI(v: string | undefined) {
            if (!v || typeof v !== "string") {
              return "";
            }
            try {
              const d = parse(v, "yyyy-MM-dd", Date.now());
              return format(d, pattern);
            } catch (e) {
              console.error(`toStringUI: failed to convert ${v}: ${e}`);
              return "";
            }
          },
          fromStringUI: (v: string | undefined): string => {
            if (!v) {
              return "";
            }
            try {
              const t_ms = parse(v, pattern, Date.now()).getTime();
              return format(t_ms, "yyyy-MM-dd");
            } catch (e) {
              console.error(`fromStringUI: failed to convert ${v}`);
              return "";
            }
          },
        }}
      />
      {open && (
        <Dialog onClose={() => setOpen(false)}>
          <Calendar
            value={AbsoluteTime.fromMilliseconds(calendarOpenTime)}
            onChange={(v) => {
              // The date is always *stored* as an ISO date.
              // !!!!! why?
              /**
               * form and fields should only care about how the information is asked 
               * to the user and should not care about how is store or sent to the server
               * 
               * this format here should always be the 'pattern' of the field
               */
              onChange(
                v.t_ms === "never" ? undefined : format(v.t_ms, "yyyy-MM-dd"),
              );
              setOpen(false);
            }}
          />
        </Dialog>
      )}
    </Fragment>
  );
}
