/*
 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 {
  AbsoluteTime,
  AmlDecisionRequest,
  assertUnreachable,
  HttpStatusCode,
  MeasureInformation,
  opEmptySuccess,
  opFixedSuccess,
  parsePaytoUri,
  PaytoString,
  stringifyPaytoUri,
  TalerError,
  TOPS_AmlEventsName,
} from "@gnu-taler/taler-util";
import {
  Attention,
  Button,
  LocalNotificationBanner,
  useExchangeApiContext,
  useLocalNotificationHandler,
  useTranslationContext,
} from "@gnu-taler/web-util/browser";
import { Fragment, h, VNode } from "preact";
import { useState } from "preact/hooks";
import {
  CurrentMeasureTable,
} from "../../components/MeasuresTable.js";
import { ShowDecisionLimitInfo } from "../../components/ShowDecisionLimitInfo.js";
import { useCurrentDecisionRequest } from "../../hooks/decision-request.js";
import { useOfficer } from "../../hooks/officer.js";
import { useServerMeasures } from "../../hooks/server-info.js";
import { computeMeasureInformation, UiMeasureInformation } from "../../utils/computeAvailableMesaures.js";
import {
  isAttributesCompleted,
  isEventsCompleted,
  isJustificationCompleted,
  isJustificationCompletedForNewACcount,
  isMeasuresCompleted,
  isPropertiesCompleted,
  isRulesCompleted,
  WizardSteps,
} from "../DecisionWizard.js";

const TALER_SCREEN_ID = 102;
/**
 * Mark for further investigation and explain decision
 * @param param0
 * @returns
 */
export function Summary({
  account,
  onMove,
  newPayto,
}: {
  account?: string;
  newPayto?: string;
  onMove: (n: WizardSteps | undefined) => void;
}): VNode {
  const { i18n } = useTranslationContext();
  const [decision, , cleanUpDecision] = useCurrentDecisionRequest();
  const measures = useServerMeasures();
  const [notification, withErrorHandler] = useLocalNotificationHandler();
  const officer = useOfficer();
  const session = officer.state === "ready" ? officer.account : undefined;
  const allMeasures = computeMeasureInformation(
    !measures || measures instanceof TalerError || measures.type === "fail"
      ? undefined
      : measures.body,
  );

  const d = decision.new_measures === undefined ? [] : decision.new_measures;
  const activeMeasureInfo: UiMeasureInformation = {
    forms: allMeasures.forms.filter((m) => d.indexOf(m.name) !== -1),
    procedures: allMeasures.procedures.filter((m) => d.indexOf(m.name) !== -1),
    info: allMeasures.info.filter((m) => d.indexOf(m.name) !== -1),
  };
  // preserve-investigate

  const { lib } = useExchangeApiContext();

  const isNewAccount = !!newPayto;
  const INVALID_ACCOUNT = !account;
  const INVALID_RULES = !isRulesCompleted(decision); //!decision.deadline || !decision.rules;
  const INVALID_MEASURES = !isMeasuresCompleted(decision); //.new_measures === undefined;
  const INVALID_PROPERTIES = !isPropertiesCompleted(decision); //.properties === undefined;
  const INVALID_EVENTS = !isEventsCompleted(decision); //false; //decision.inhibit_events === undefined;
  const INVALID_JUSTIFICATION = isNewAccount
    ? !isJustificationCompletedForNewACcount(decision)
    : !isJustificationCompleted(decision);
  const INVALID_ATTRIBUTES = !isAttributesCompleted(decision);

  const MROS_REPORT_COMPLETED = !decision.triggering_events
    ? false
    : decision.triggering_events.includes(
        TOPS_AmlEventsName.MROS_REPORTED_SUSPICION_SIMPLE,
      ) ||
      decision.triggering_events.includes(
        TOPS_AmlEventsName.MROS_REPORTED_SUSPICION_SUBSTANTIATED,
      );

  const CANT_SUBMIT =
    INVALID_ACCOUNT ||
    INVALID_RULES ||
    INVALID_MEASURES ||
    INVALID_PROPERTIES ||
    INVALID_EVENTS ||
    INVALID_JUSTIFICATION ||
    INVALID_ATTRIBUTES;

  function clearUp() {
    cleanUpDecision();
    onMove(undefined);
  }

  const fullPayto = !newPayto ? undefined : parsePaytoUri(newPayto);
  if (fullPayto && decision.accountName) {
    fullPayto.params["receiver-name"] = decision.accountName;
  }

  const submitHandler =
    CANT_SUBMIT || !session
      ? undefined
      : withErrorHandler(
          () => {
            const request: Omit<AmlDecisionRequest, "officer_sig"> = {
              h_payto: account,
              decision_time: AbsoluteTime.toProtocolTimestamp(
                AbsoluteTime.now(),
              ),
              justification: decision.justification!,
              payto_uri: !fullPayto ? undefined : stringifyPaytoUri(fullPayto),
              keep_investigating: decision.keep_investigating ?? false,
              new_rules: {
                expiration_time: AbsoluteTime.toProtocolTimestamp(
                  decision.deadline!,
                ),
                rules: decision.rules!,
                successor_measure: decision.onExpire_measure,
                custom_measures: decision.custom_measures ?? {},
              },
              attributes_expiration: decision.attributes?.expiration
                ? AbsoluteTime.toProtocolTimestamp(
                    decision.attributes.expiration,
                  )
                : undefined,
              events: decision.triggering_events,
              attributes: decision.attributes?.data,
              properties: decision.properties!,
              new_measures:
                !decision.new_measures || !decision.new_measures.length
                  ? undefined
                  : decision.new_measures.join(" "),
            };
            return lib.exchange.makeAmlDesicion(session, request);
          },
          (suc) => {
            clearUp();
          },
          (fail) => {
            switch (fail.case) {
              case HttpStatusCode.Forbidden:
                if (session) {
                  return i18n.str`Wrong credentials for "${session}"`;
                } else {
                  return i18n.str`Wrong credentials.`;
                }
              case HttpStatusCode.NotFound:
                return i18n.str`The account was not found`;
              case HttpStatusCode.Conflict:
                return i18n.str`Officer disabled or more recent decision was already submitted.`;
              default:
                assertUnreachable(fail.case);
            }
          },
        );

  const requiresConfirmation = MROS_REPORT_COMPLETED;
  if (submitHandler && requiresConfirmation) {
    const originalHandler = submitHandler.onClick!;
    submitHandler.onClick = async function d() {
      if (!submitConfirmation) {
        setSubmitConfirmation(true);
        return;
      }
      return originalHandler();
    };
  }

  const [submitConfirmation, setSubmitConfirmation] = useState<boolean>(false);

  if (submitConfirmation) {
    return (
      <div class="flex">
        <div class="overflow-hidden rounded-lg bg-white shadow-lg w-500 w-64 m-auto">
          <div class="px-4 py-5 sm:p-6">
            <Attention type="warning" title={i18n.str`Confirmation required`}>
              <i18n.Translate>
                This decision declares that the MROS report have concluded.
                Please make that this is the case
              </i18n.Translate>
            </Attention>

            <div class="mt-2 flex justify-between">
              <button
                onClick={() => {
                  setSubmitConfirmation(false);
                }}
                class="mt-4 disabled:opacity-50 disabled:cursor-default rounded-md  px-3 py-2 text-sm font-semibold  shadow-sm  focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 "
              >
                <i18n.Translate>I want to check first!</i18n.Translate>
              </button>
              <Button
                type="submit"
                handler={submitHandler}
                disabled={!submitHandler}
                class="mt-4 disabled:opacity-50 disabled:cursor-default 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 decision</i18n.Translate>
              </Button>
            </div>
          </div>
        </div>
      </div>
    );
  }

  return (
    <Fragment>
      <LocalNotificationBanner notification={notification} />
      {INVALID_RULES ? (
        <Fragment>
          {!decision.deadline && (
            <Attention
              type="danger"
              title={i18n.str`Missing deadline`}
              onClose={() => onMove("justification")}
            >
              <i18n.Translate>
                Deadline should specify when this rules ends and what is the
                next measures to apply after expiration.
              </i18n.Translate>
            </Attention>
          )}
          {!decision.rules && (
            <Attention
              type="danger"
              title={i18n.str`Missing rules`}
              onClose={() => onMove("rules")}
            >
              <i18n.Translate>
                Can't make a decision without rules.
              </i18n.Translate>
            </Attention>
          )}
        </Fragment>
      ) : (
        <div>
          <h2 class="mt-4 mb-2">
            <i18n.Translate>New rules</i18n.Translate>
          </h2>
          <ShowDecisionLimitInfo
            fixed
            since={AbsoluteTime.now()}
            until={decision.deadline!}
            rules={decision.rules!}
            startOpen
            measure={decision.onExpire_measure ?? ""}
          />
        </div>
      )}
      {INVALID_MEASURES ? (
        <Attention
          type="danger"
          title={i18n.str`Missing active measure`}
          onClose={() => onMove("measures")}
        >
          <i18n.Translate>
            You should specify in the measure section.
          </i18n.Translate>
        </Attention>
      ) : decision.new_measures!.length === 0 ? (
        <Attention
          type="info"
          title={i18n.str`No customer action required.`}
          onClose={() => onMove("measures")}
        >
          <i18n.Translate>No active measure has been selected.</i18n.Translate>
        </Attention>
      ) : (
        <CurrentMeasureTable measures={activeMeasureInfo} />
      )}
      {INVALID_PROPERTIES ? (
        <Attention
          type="danger"
          title={i18n.str`Missing properties`}
          onClose={() => onMove("properties")}
        >
          <i18n.Translate>
            You should specify in the properties section.
          </i18n.Translate>
        </Attention>
      ) : (
        <div />
      )}
      {INVALID_EVENTS ? (
        <Attention
          type="danger"
          title={i18n.str`Missing events`}
          onClose={() => onMove("events")}
        >
          <i18n.Translate>
            You should specify in the events section.
          </i18n.Translate>
        </Attention>
      ) : (
        <div />
      )}
      {INVALID_JUSTIFICATION ? (
        <Attention
          type="danger"
          title={i18n.str`Missing justification`}
          onClose={() => onMove("justification")}
        >
          <i18n.Translate>
            You must complete the justification section.
          </i18n.Translate>
        </Attention>
      ) : (
        <div />
      )}
      {INVALID_ATTRIBUTES ? (
        <Attention
          type="danger"
          title={i18n.str`Invalid attributes`}
          onClose={() => onMove("attributes")}
        >
          <i18n.Translate>
            You should check form errors or submit a decision without
            attributes.
          </i18n.Translate>
        </Attention>
      ) : (
        <div />
      )}

      <div class="mt-2 flex justify-between">
        <button
          onClick={clearUp}
          class="mt-4 disabled:opacity-50 disabled:cursor-default rounded-md  px-3 py-2 text-sm font-semibold  shadow-sm  focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 "
        >
          <i18n.Translate>Clear</i18n.Translate>
        </button>
        <Button
          type="submit"
          handler={submitHandler}
          disabled={!submitHandler}
          class="mt-4 disabled:opacity-50 disabled:cursor-default 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>Send decision</i18n.Translate>
        </Button>
      </div>
    </Fragment>
  );
}
/**
 * FIXME: this should be removed when the server allows null programs
 * or we define a no-operation program
 *
 * https://bugs.gnunet.org/view.php?id=9810
 * https://bugs.gnunet.org/view.php?id=9874
 *
 * @param measures
 * @returns
//  */
// function workaround_defaultProgramName(
//   measures: Record<string, MeasureInformation>,
// ) {
//   const ms = Object.keys(measures);
//   for (const name of ms) {
//     const m = measures[name];
//     if (!m.prog_name) {
//       measures[name].prog_name = "preserve-investigate";
//     }
//   }
//   return measures;
// }
