/*
 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,
  encodeCrock,
  HttpStatusCode,
  opEmptySuccess,
  Paytos,
  succeedOrThrow,
  TalerError,
  TOPS_AmlEventsName,
} from "@gnu-taler/taler-util";
import {
  Attention,
  ButtonBetter,
  LocalNotificationBanner,
  useExchangeApiContext,
  useLocalNotificationBetter,
  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 { OfficerReady } 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,
  officer,
}: {
  account?: string;
  newPayto?: string;
  officer: OfficerReady;
  onMove: (n: WizardSteps | undefined) => void;
}): VNode {
  const { i18n } = useTranslationContext();
  const [decision, , cleanUpDecision] = useCurrentDecisionRequest();
  const measures = useServerMeasures();

  const [notification, safeFunctionHandler] = useLocalNotificationBetter();

  const session = officer.account;
  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 : Paytos.fromString(newPayto);
  if (fullPayto && fullPayto.type === "ok" && decision.accountName) {
    fullPayto.body.params["receiver-name"] = decision.accountName;
  }

  const request: undefined | Omit<AmlDecisionRequest, "officer_sig"> =
    CANT_SUBMIT
      ? undefined
      : {
          h_payto: account!,
          decision_time: AbsoluteTime.toProtocolTimestamp(AbsoluteTime.now()),
          justification: decision.justification!,
          payto_uri:
            !fullPayto || fullPayto.type === "fail"
              ? undefined
              : Paytos.toFullString(fullPayto.body),
          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(" "),
        };

  const [submitConfirmation, setSubmitConfirmation] = useState<boolean>(false);
  const requiresConfirmation = MROS_REPORT_COMPLETED;
  const submit = safeFunctionHandler(
    async (req) => {
      if (requiresConfirmation && !submitConfirmation) {
        setSubmitConfirmation(true);
        return opEmptySuccess();
      }
      return lib.exchange.makeAmlDesicion(session, req);
    },
    !request ? undefined : [request],
  );
  submit.onSuccess = clearUp;

  submit.onFail = (fail) => {
    switch (fail.case) {
      case HttpStatusCode.Forbidden:
        return i18n.str`Wrong credentials for "${session}"`;
      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);
    }
  };

  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>
              <ButtonBetter
                type="submit"
                onClick={submit}
                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>
              </ButtonBetter>
            </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>
        <ButtonBetter
          type="submit"
          onClick={submit}
          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>
        </ButtonBetter>
      </div>
    </Fragment>
  );
}
