/*
 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 {
  assertUnreachable,
  parsePaytoUri,
  PaytoString,
  PaytoUri,
  TalerError,
  TranslatedString
} from "@gnu-taler/taler-util";
import { CopyButton, useTranslationContext } from "@gnu-taler/web-util/browser";
import { h, VNode } from "preact";
import {
  DecisionRequest,
  useCurrentDecisionRequest,
} from "../hooks/decision-request.js";
import { useAccountActiveDecision } from "../hooks/decisions.js";
import { Events } from "./decision/Events.js";
import { Attributes } from "./decision/Information.js";
import { Justification } from "./decision/Justification.js";
import { Measures } from "./decision/Measures.js";
import { Properties } from "./decision/Properties.js";
import { Rules } from "./decision/Rules.js";
import { Summary } from "./decision/Summary.js";

const TALER_SCREEN_ID = 113;

export type WizardSteps =
  | "attributes" // submit more information
  | "rules" // define the limits
  | "measures" // define a new form/challenge
  | "properties" // define account information
  | "events" // define events to trigger
  | "justification" // finalize, investigate?;
  | "summary";

const STEPS_ORDER: WizardSteps[] = [
  "attributes",
  "rules",
  "properties",
  "events",
  "measures",
  "justification",
  "summary",
];

const STEPS_ORDER_MAP = STEPS_ORDER.reduce(
  (map, cur, idx, steps) => {
    map[cur] = {
      prev: idx === 0 ? undefined : steps[idx - 1],
      next: idx === steps.length ? undefined : steps[idx + 1],
    };
    return map;
  },
  {} as {
    [s in WizardSteps]: {
      next: WizardSteps | undefined;
      prev: WizardSteps | undefined;
    };
  },
);

export function isRulesCompleted(request: DecisionRequest): boolean {
  return request.rules !== undefined && request.deadline !== undefined;
}
export function isAttributesCompleted(request: DecisionRequest): boolean {
  return (
    request.attributes === undefined || request.attributes.errors === undefined
  );
}
export function isPropertiesCompleted(request: DecisionRequest): boolean {
  return (
    request.properties !== undefined && request.properties_errors === undefined
  );
}
export function isEventsCompleted(request: DecisionRequest): boolean {
  return request.custom_events !== undefined;
}
export function isMeasuresCompleted(request: DecisionRequest): boolean {
  return request.new_measures !== undefined;
}
export function isJustificationCompleted(request: DecisionRequest): boolean {
  return request.keep_investigating !== undefined && !!request.justification;
}
export function isJustificationCompletedForNewACcount(
  request: DecisionRequest,
): boolean {
  return (
    request.keep_investigating !== undefined &&
    !!request.justification &&
    !!request.accountName
  );
}

export function DecisionWizard({
  account,
  newPayto,
  step,
  formId,
  onMove,
}: {
  account: string;
  newPayto?: string;
  formId: string | undefined;
  step?: WizardSteps;
  onMove: (n: WizardSteps | undefined) => void;
}): VNode {
  const { i18n } = useTranslationContext();
  const stepOrDefault = step ?? STEPS_ORDER[0];
  const content = (function () {
    switch (stepOrDefault) {
      case "rules":
        return <Rules newPayto={newPayto} />;
      case "properties":
        return <Properties />;
      case "events":
        return <Events />;
      case "measures":
        return <Measures />;
      case "justification":
        return <Justification newPayto={newPayto} />;
      case "attributes":
        return <Attributes formId={formId} />;
      case "summary":
        return (
          <Summary account={account} onMove={onMove} newPayto={newPayto} />
        );
    }
    assertUnreachable(stepOrDefault);
  })();

  return (
    <div class="min-w-60">
      <header class="flex items-center justify-between border-b border-white/5 px-4 py-4 sm:px-6 sm:py-6 lg:px-8">
        <Header account={account} newPayto={newPayto} />
        <div>{account}</div>
        <CopyButton class="" getContent={() => account} />
      </header>

      <WizardSteps
        step={stepOrDefault}
        onMove={onMove}
        newAccount={!!newPayto}
      />
      <button
        disabled={!STEPS_ORDER_MAP[stepOrDefault].prev}
        onClick={() => {
          onMove(STEPS_ORDER_MAP[stepOrDefault].prev);
        }}
        class="m-4  rounded-md w-fit border-0 px-3 py-2 text-center text-sm disabled:bg-gray-500 bg-indigo-700 text-white shadow-sm hover:bg-indigo-700"
      >
        <i18n.Translate>Prev</i18n.Translate>
      </button>
      <button
        disabled={!STEPS_ORDER_MAP[stepOrDefault].next}
        onClick={() => {
          onMove(STEPS_ORDER_MAP[stepOrDefault].next);
        }}
        class="m-4  rounded-md w-fit border-0 px-3 py-2 text-center text-sm disabled:bg-gray-500 bg-indigo-700 text-white shadow-sm hover:bg-indigo-700"
      >
        <i18n.Translate>Next</i18n.Translate>
      </button>
      {content}
    </div>
  );
}
function WizardSteps({
  step: currentStep,
  onMove,
  newAccount,
}: {
  step: WizardSteps;
  onMove: (n: WizardSteps | undefined) => void;
  newAccount: boolean;
}): VNode {
  const [request] = useCurrentDecisionRequest();
  const { i18n } = useTranslationContext();
  const STEP_INFO: {
    [s in WizardSteps]: {
      label: TranslatedString;
      description: TranslatedString;
      isCompleted: (r: DecisionRequest) => boolean;
    };
  } = {
    attributes: {
      label: i18n.str`Attributes`,
      description: i18n.str`Add more information about the customer`,
      isCompleted: isAttributesCompleted,
    },
    rules: {
      label: i18n.str`Rules`,
      description: i18n.str`Set the limit of the operations`,
      isCompleted: isRulesCompleted,
    },
    events: {
      label: i18n.str`Events`,
      description: i18n.str`Trigger notifications.`,
      isCompleted: isEventsCompleted,
    },
    measures: {
      label: i18n.str`Measures`,
      description: i18n.str`Ask the customer to take action.`,
      isCompleted: isMeasuresCompleted,
    },
    justification: {
      label: i18n.str`Justification`,
      description: i18n.str`Describe the decision.`,
      isCompleted: newAccount
        ? isJustificationCompletedForNewACcount
        : isJustificationCompleted,
    },
    properties: {
      label: i18n.str`Properties`,
      description: i18n.str`Flag the current account state.`,
      isCompleted: isPropertiesCompleted,
    },
    summary: {
      label: i18n.str`Summary`,
      description: i18n.str`Review and submit.`,
      isCompleted: () => false,
    },
  };
  return (
    <div class="lg:border-b lg:border-t lg:border-gray-200">
      <nav class="mx-auto max-w-7xl " aria-label="Progress">
        <ol
          role="list"
          class="overflow-hidden rounded-md lg:flex lg:rounded-none lg:border-l lg:border-r lg:border-gray-200"
        >
          {STEPS_ORDER.map((stepLabel, k) => {
            const info = STEP_INFO[stepLabel];
            const st = info.isCompleted(request)
              ? "completed"
              : currentStep === stepLabel
                ? "current"
                : "incomplete";

            const pos = !STEPS_ORDER_MAP[stepLabel].prev
              ? "first"
              : !STEPS_ORDER_MAP[stepLabel].next
                ? "last"
                : "middle";

            return (
              <li class="relative lg:flex-1" key={k}>
                <div
                  data-pos={pos}
                  class="overflow-hidden data-[pos=first]:rounded-t-md border data-[pos=first]:border-b-0 border-gray-200 lg:border-0"
                >
                  {currentStep === stepLabel ? (
                    <span
                      class="absolute left-0 top-0 h-full w-1 bg-indigo-600 lg:bottom-0 lg:top-auto lg:h-1 lg:w-full"
                      aria-hidden="true"
                    ></span>
                  ) : undefined}
                  <button
                    aria-current="step"
                    class="group"
                    onClick={() => {
                      onMove(stepLabel);
                    }}
                  >
                    <span
                      data-status={st}
                      class="absolute left-0 top-0 h-full w-1 data-[status=current]:bg-indigo-600 data-[status=current]:bg-transparent group-hover:bg-gray-200  lg:bottom-0 lg:top-auto lg:h-1 lg:w-full"
                      aria-hidden="true"
                    ></span>
                    <div>
                      <span class="flex items-start px-4 pt-4 text-sm font-medium">
                        <span class="shrink-0">
                          <span
                            data-status={st}
                            class="flex size-6 items-center justify-center rounded-full data-[status=completed]:bg-indigo-600 border-2 data-[status=current]:border-indigo-600 data-[status=incomplete]:border-gray-300"
                          >
                            <svg
                              class="size-4 text-white "
                              viewBox="0 0 24 24"
                              fill="currentColor"
                              aria-hidden="true"
                              data-slot="icon"
                            >
                              <path
                                fill-rule="evenodd"
                                d="M19.916 4.626a.75.75 0 0 1 .208 1.04l-9 13.5a.75.75 0 0 1-1.154.114l-6-6a.75.75 0 0 1 1.06-1.06l5.353 5.353 8.493-12.74a.75.75 0 0 1 1.04-.207Z"
                                clip-rule="evenodd"
                              />
                            </svg>
                          </span>
                        </span>
                        <span
                          data-status={st}
                          class="ml-4 data-[status=current]:text-indigo-600"
                        >
                          {info.label}
                        </span>
                      </span>
                    </div>
                    <div class="p-2 text-start">
                      <span class="ml-4 mt-0.5 flex min-w-0 flex-col">
                        <span
                          data-current={currentStep === stepLabel}
                          class="text-sm font-medium data-[current=true]:text-indigo-600"
                        ></span>
                        <span class="text-sm font-medium text-gray-500">
                          {info.description}
                        </span>
                      </span>
                    </div>
                  </button>
                  {pos === "first" ? undefined : (
                    <div
                      data-pos={pos}
                      class="absolute inset-0 left-0 top-0 hidden w-2 lg:block"
                      aria-hidden="true"
                    >
                      <svg
                        data-pos={pos}
                        class="size-full text-gray-300 data-[pos=middle]:h-full data-[pos=middle]:w-full"
                        viewBox="0 0 12 82"
                        fill="none"
                        preserveAspectRatio="none"
                      >
                        <path
                          d="M0.5 0V31L10.5 41L0.5 51V82"
                          stroke="currentcolor"
                          vector-effect="non-scaling-stroke"
                        />
                      </svg>
                    </div>
                  )}
                </div>
              </li>
            );
          })}
        </ol>
      </nav>
    </div>
  );
}

function Header({
  newPayto,
  account,
}: {
  account: string;
  newPayto: string | undefined;
}): VNode {
  const { i18n } = useTranslationContext();
  const isNewAccount = !!newPayto;

  let newPaytoParsed: PaytoUri | undefined;
  const isNewAccountAWallet =
    newPayto === undefined
      ? undefined
      : (newPaytoParsed = parsePaytoUri(newPayto)) === undefined
        ? undefined
        : newPaytoParsed.isKnown &&
          (newPaytoParsed.targetType === "taler-reserve" ||
            newPaytoParsed.targetType === "taler-reserve-http");

  const activeDecision = useAccountActiveDecision(
    isNewAccount ? undefined : account,
  );

  const info =
    !activeDecision ||
    activeDecision instanceof TalerError ||
    activeDecision.type === "fail"
      ? undefined
      : activeDecision.body;

  if (!info && !isNewAccount) {
    <h1 class="text-base font-semibold leading-7 text-black">
      <i18n.Translate>loading... </i18n.Translate>
    </h1>;
  }
  // info may be undefined if this is a new account
  // for which we use the payto:// parameter
  const isWallet = info?.is_wallet ?? isNewAccountAWallet;

  if (isWallet === undefined) {
    return (
      <h1 class="text-base font-semibold leading-7 text-black">
        <i18n.Translate>Decision for account: </i18n.Translate>
      </h1>
    );
  }
  if (isWallet) {
    return (
      <h1 class="text-base font-semibold leading-7 text-black">
        <i18n.Translate>Decision for wallet: </i18n.Translate>
      </h1>
    );
  } else {
    return (
      <h1 class="text-base font-semibold leading-7 text-black">
        <i18n.Translate>Decision for bank account: </i18n.Translate>
      </h1>
    );
  }
}
