/*
 This file is part of GNU Taler
 (C) 2022-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/>
 */
import {
  AbsoluteTime,
  AmountJson,
  Amounts,
  Codec,
  OperationFail,
  OperationOk,
  TalerErrorDetail,
  buildCodecForObject,
  codecForNumber,
  codecForString,
  codecOptional,
} from "@gnu-taler/taler-util";
import {
  DefaultForm,
  useExchangeApiContext,
  useTranslationContext,
} from "@gnu-taler/web-util/browser";
import { h } from "preact";
import { BaseForm, FormMetadata, uiForms } from "../forms/declaration.js";
import { AmlExchangeBackend } from "../utils/types.js";
import { privatePages } from "../Routing.js";

export function AntiMoneyLaunderingForm({
  account,
  formId,
  onSubmit,
}: {
  account: string;
  formId: string;
  onSubmit: (
    justification: Justification,
    state: AmlExchangeBackend.AmlState,
    threshold: AmountJson,
  ) => Promise<void>;
}) {
  const { i18n } = useTranslationContext();
  const theForm = uiForms.forms(i18n).find((v) => v.id === formId);
  if (!theForm) {
    return <div>form with id {formId} not found</div>;
  }

  const { config } = useExchangeApiContext();

  const initial = {
    when: AbsoluteTime.now(),
    state: AmlExchangeBackend.AmlState.pending,
    threshold: Amounts.zeroOfCurrency(config.currency),
  };
  return (
    <DefaultForm
      initial={initial}
      form={theForm.impl(initial)}
      onUpdate={() => {}}
      onSubmit={(formValue) => {
        if (
          formValue.state === undefined ||
          formValue.threshold === undefined
        ) {
          return;
        }
        const validatedForm = formValue as BaseForm;
        const st = formValue.state;
        const amount = formValue.threshold;

        const justification: Justification = {
          id: theForm.id,
          label: theForm.label,
          version: theForm.version,
          value: validatedForm,
        };

        onSubmit(justification, st, amount);
      }}
    >
      <div class="mt-6 flex items-center justify-end gap-x-6">
        <a
          href={privatePages.caseDetails.url({ cid: account })}
          class="text-sm font-semibold leading-6 text-gray-900"
        >
          <i18n.Translate>Cancel</i18n.Translate>
        </a>
        <button
          type="submit"
          class="rounded-md bg-indigo-600 px-3 py-2 text-sm font-semibold text-white shadow-sm hover:bg-indigo-500 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-indigo-600"
        >
          <i18n.Translate>Confirm</i18n.Translate>
        </button>
      </div>
    </DefaultForm>
  );
}

export type Justification<T extends BaseForm = BaseForm> = {
  // form values
  value: T;
} & Omit<Omit<FormMetadata<BaseForm>, "icon">, "impl">;

export function stringifyJustification(j: Justification): string {
  return JSON.stringify(j);
}

type SimpleFormMetadata = {
  version?: number;
  id?: string;
};

export const codecForSimpleFormMetadata = (): Codec<SimpleFormMetadata> =>
  buildCodecForObject<SimpleFormMetadata>()
    .property("id", codecOptional(codecForString()))
    .property("version", codecOptional(codecForNumber()))
    .build("SimpleFormMetadata");

type ParseJustificationFail =
  | "not-json"
  | "id-not-found"
  | "form-not-found"
  | "version-not-found";

export function parseJustification(
  s: string,
  listOfAllKnownForms: FormMetadata<BaseForm>[],
):
  | OperationOk<{
      justification: Justification;
      metadata: FormMetadata<BaseForm>;
    }>
  | OperationFail<ParseJustificationFail> {
  try {
    const justification = JSON.parse(s);
    const info = codecForSimpleFormMetadata().decode(justification);
    if (!info.id) {
      return {
        type: "fail",
        case: "id-not-found",
        detail: {} as TalerErrorDetail,
      };
    }
    if (!info.version) {
      return {
        type: "fail",
        case: "version-not-found",
        detail: {} as TalerErrorDetail,
      };
    }
    const found = listOfAllKnownForms.find((f) => {
      return f.id === info.id && f.version === info.version;
    });
    if (!found) {
      return {
        type: "fail",
        case: "form-not-found",
        detail: {} as TalerErrorDetail,
      };
    }
    return {
      type: "ok",
      body: {
        justification,
        metadata: found,
      },
    };
  } catch (e) {
    return {
      type: "fail",
      case: "not-json",
      detail: {} as TalerErrorDetail,
    };
  }
}
