/*
 This file is part of GNU Taler
 (C) 2022-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/>
 */
import { TalerFormAttributes } from "@gnu-taler/taler-util";
import {
  ErrorsSummary,
  FormDesign,
  FormMetadata,
  FormUI,
  InputAbsoluteTime,
  InternationalizationAPI,
  onComponentUnload,
  UIFieldHandler,
  useForm,
  useTranslationContext,
} from "@gnu-taler/web-util/browser";
import { h, VNode } from "preact";
import { useEffect, useMemo, useState } from "preact/compat";
import { useUiFormsContext } from "../../context/ui-forms.js";
import { useCurrentDecisionRequest } from "../../hooks/decision-request.js";

const TALER_SCREEN_ID = 107;

/**
 * Mark for further investigation and explain decision
 * @param param0
 * @returns
 */
export function Attributes({
  formId: defaultForm,
}: {
  formId: string | undefined;
}): VNode {
  const [request] = useCurrentDecisionRequest();

  const formByState = request.attributes?.formId;
  const [selectedFormId, setSelectedFormId] = useState<string | undefined>(
    formByState ?? defaultForm,
  );
  const { forms } = useUiFormsContext();

  const theForm = !selectedFormId
    ? undefined
    : findLatestVersionOfFormWithId(forms, selectedFormId);

  if (!theForm) {
    return (
      <SelectForm
        forms={forms}
        onSelectForm={(f) => {
          setSelectedFormId(f);
        }}
      />
    );
  }

  return (
    <FillCustomerData
      theForm={theForm}
      changeForm={() => {
        setSelectedFormId(undefined);
      }}
    />
  );
}

function FillCustomerData({
  theForm,
  changeForm,
}: {
  theForm: FormMetadata;
  changeForm: () => void;
}): VNode {
  const [request, updateRequest] = useCurrentDecisionRequest();
  const [expiration, setExpiration] = useState(request.attributes?.expiration);
  const expirationHandler: UIFieldHandler = {
    onChange: setExpiration,
    value: expiration,
    name: "expiration",
  };

  // FIXME: What about forms with context?
  let formDesign: FormDesign;
  if (typeof theForm.config === "function") {
    const config = theForm.config;
    formDesign = useMemo(() => config({}), [theForm]);
  } else {
    formDesign = theForm.config;
  }

  /* FIXME: Should we always add FORM ID and VERSION into data? */
  const form = useForm<object>(formDesign, request.attributes?.data ?? {});

  const data = {
    ...form.status.result,
    [TalerFormAttributes.FORM_ID]: theForm.id,
    [TalerFormAttributes.FORM_VERSION]: theForm.version,
  };

  const errors = form.status.errors;

  onComponentUnload(() => {
    updateRequest("unload info", {
      attributes: {
        data,
        expiration,
        formId: theForm.id,
        formVersion: theForm.version,
        errors,
      },
    });
  });
  const { i18n } = useTranslationContext();

  return (
    <div>
      <div class="sm:flex sm:items-center">
        <div class="sm:flex-auto">
          <h1 class="text-base font-semibold text-gray-900">
            <i18n.Translate>Complete the form</i18n.Translate>
          </h1>
          <p class="mt-2 text-sm text-gray-700">
            <i18n.Translate>
              These measures will ask the customer for information.
            </i18n.Translate>
          </p>
        </div>
      </div>

      <div class="flex flex-column justify-between my-4">
        <div>
          <h1>
            Form: {theForm.id} ({theForm.version})
          </h1>
          <a
            class="text-indigo-700 cursor-pointer p-2 text-sm leading-6 font-semibold"
            onClick={() => {
              changeForm();
            }}
          >
            <i18n.Translate>change form</i18n.Translate>
          </a>
        </div>
        <InputAbsoluteTime
          label={i18n.str`Expiration`}
          help={i18n.str`Expiration date of the information filled in this form.`}
          name="expiration"
          pattern="dd/MM/yyyy"
          handler={expirationHandler}
        />
      </div>
      <div>
        {!errors ? undefined : <ErrorsSummary errors={errors} />}
        <FormUI design={formDesign} model={form.model} />
      </div>
    </div>
  );
}

function SelectForm({
  forms,
  onSelectForm,
}: {
  forms: FormMetadata[];
  onSelectForm: (id: string | undefined) => void;
}): VNode {
  const { i18n } = useTranslationContext();
  const design = formDesign(i18n, forms);
  const [, updateRequest] = useCurrentDecisionRequest();

  const form = useForm<SelectFormType>(design, {
    formId: undefined,
  });

  const fid = form.status.result?.formId;

  useEffect(() => {
    onSelectForm(fid);
  }, [fid]);

  onComponentUnload(() => {
    updateRequest("unload info no form", {
      attributes: undefined,
    });
  });

  return (
    <div>
      <FormUI design={design} model={form.model} />
    </div>
  );
}

type SelectFormType = {
  formId: string | undefined;
};

const formDesign = (
  i18n: InternationalizationAPI,
  mi: FormMetadata[],
): FormDesign => ({
  type: "double-column",
  sections: [
    {
      title: i18n.str`Include new information`,
      description: i18n.str`Select a form to submit new information about the customer or continue to the next step`,
      fields: [
        {
          id: "formId",
          type: "selectOne",
          label: i18n.str`Form:`,
          choices: mi.map((f) => ({
            label: f.label,
            value: f.id,
          })),
        },
      ],
    },
  ],
});

function findLatestVersionOfFormWithId(
  forms: FormMetadata[],
  formId: string,
): FormMetadata | undefined {
  const found = forms.filter((v) => v.id === formId);
  if (!found.length) return undefined;
  if (found.length === 1) return found[0];
  return found.sort((a, b) => b.version - a.version)[0];
}
