/*
 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 {
  HttpStatusCode,
  createRFC8959AccessTokenEncoded,
  encodeCrock,
  randomBytes,
} from "@gnu-taler/taler-util";
import {
  Button,
  ButtonBetter,
  LocalNotificationBanner,
  ShowInputErrorLabel,
  useChallengerApiContext,
  useLocalNotificationBetter,
  useTranslationContext,
} from "@gnu-taler/web-util/browser";
import { Fragment, VNode, h } from "preact";
import { useState } from "preact/hooks";
import { safeToURL } from "../Routing.js";
import { useSessionState } from "../hooks/session.js";
import { doAutoFocus, undefinedIfEmpty } from "./AnswerChallenge.js";
import { AccessToken } from "@gnu-taler/taler-util";

type Props = {
  clientId: string;
  secret: string | undefined;
  redirectURL: URL | undefined;
  onCreated: () => void;
  focus?: boolean;
};
export function Setup({
  clientId,
  secret,
  redirectURL,
  focus,
  onCreated,
}: Props): VNode {
  const { i18n } = useTranslationContext();
  const [notification, safeFunctionHandler] = useLocalNotificationBetter();
  const { lib } = useChallengerApiContext();
  const { start } = useSessionState();
  const [password, setPassword] = useState<string | undefined>(secret);
  const [url, setUrl] = useState<string | undefined>(redirectURL?.href);

  const errors = undefinedIfEmpty({
    password: !password ? i18n.str`Required` : undefined,
    url: !url
      ? i18n.str`Required`
      : !safeToURL(url)
        ? i18n.str`Invalid format`
        : undefined,
  });

  const doStart = safeFunctionHandler(
    (token: AccessToken, url) => lib.challenger.setup(clientId, token),
    !!errors || password === undefined || url === undefined
      ? undefined
      : [createRFC8959AccessTokenEncoded(password), url],
  );
  doStart.onSuccess = (ok, token, redirect_uri) => {
    start();
    const redirect = new URL(window.location.href);
    redirect.searchParams.set("client_id", clientId);
    redirect.searchParams.set("redirect_uri", redirect_uri);
    redirect.searchParams.set("state", encodeCrock(randomBytes(32)));
    redirect.searchParams.set("nonce", ok.nonce);
    redirect.hash = "";
    window.location.href = redirect.href;
    onCreated();
  };

  doStart.onFail = (fail) => {
    switch (fail.case) {
      case HttpStatusCode.NotFound:
        return i18n.str`The server doesn't know about this client. Either the URL or the secret is wrong.`;
    }
  };

  return (
    <Fragment>
      <LocalNotificationBanner notification={notification} />

      <div class="isolate bg-white px-6 py-12">
        <div class="mx-auto max-w-2xl text-center">
          <h2 class="text-3xl font-bold tracking-tight text-gray-900 sm:text-4xl">
            <i18n.Translate>
              Setup new challenge with client ID: &quot;{clientId}&quot;
            </i18n.Translate>
          </h2>
        </div>

        <form
          method="POST"
          class="mx-auto mt-4 max-w-xl sm:mt-20"
          onSubmit={(e) => {
            e.preventDefault();
          }}
        >
          <div class="sm:col-span-2">
            <label
              for="email"
              class="block text-sm font-semibold leading-6 text-gray-900"
            >
              <i18n.Translate>Password</i18n.Translate>
            </label>
            <div class="mt-2.5">
              <input
                type="password"
                name="password"
                id="password"
                ref={focus ? doAutoFocus : undefined}
                maxLength={512}
                autocomplete="password"
                value={password}
                onChange={(e) => {
                  setPassword(e.currentTarget.value);
                }}
                readOnly={secret !== undefined}
                class="block w-full read-only:bg-slate-200 rounded-md border-0 px-3.5 py-2 text-gray-900 shadow-sm ring-1 ring-inset ring-gray-300 placeholder:text-gray-400 focus:ring-2 focus:ring-inset focus:ring-indigo-600 sm:text-sm sm:leading-6"
              />
              <ShowInputErrorLabel
                message={errors?.password}
                isDirty={password !== undefined}
              />
            </div>
          </div>

          <div class="sm:col-span-2">
            <label
              for="email"
              class="block text-sm font-semibold leading-6 text-gray-900"
            >
              <i18n.Translate>Redirect URL</i18n.Translate>
            </label>
            <div class="mt-2.5">
              <input
                type="text"
                name="redirect_url"
                id="redirect_url"
                maxLength={512}
                autocomplete="redirect_url"
                value={url}
                onChange={(e) => {
                  setUrl(e.currentTarget.value);
                }}
                readOnly={redirectURL !== undefined}
                class="block w-full read-only:bg-slate-200 rounded-md border-0 px-3.5 py-2 text-gray-900 shadow-sm ring-1 ring-inset ring-gray-300 placeholder:text-gray-400 focus:ring-2 focus:ring-inset focus:ring-indigo-600 sm:text-sm sm:leading-6"
              />
              <ShowInputErrorLabel
                message={errors?.url}
                isDirty={url !== undefined}
              />
            </div>
          </div>
        </form>
        <div class="mt-10">
          <ButtonBetter
            type="submit"
            class="block w-full disabled:bg-gray-300 rounded-md bg-indigo-600 px-3.5 py-2.5 text-center 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"
            onClick={doStart}
          >
            <i18n.Translate>Start</i18n.Translate>
          </ButtonBetter>
        </div>
      </div>
    </Fragment>
  );
}
