/*
 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,
  ChallengerApi,
  Codec,
  buildCodecForObject,
  codecForAbsoluteTime,
  codecForAny,
  codecForList,
  codecForString,
  codecForStringURL,
  codecOptional,
} from "@gnu-taler/taler-util";
import { buildStorageKey, useLocalStorage } from "@gnu-taler/web-util/browser";

/**
 * Has the information to reach and
 * authenticate at the bank's backend.
 */
export type SessionId = {
  nonce: string;
  clientId: string;
  redirectURL: string;
  state: string;
};

export type LastChallengeResponse = {
  sendCodeLeft: number;
  changeTargetLeft: number;
  checkPinLeft: number;
  nextSend: AbsoluteTime;
  transmitted: boolean;
};

interface LastAddress {
  address: Record<string, string>;
  type: string;
  savedAt: AbsoluteTime;
}

export type SessionState = {
  completedURL: string | undefined;
  lastAddress: Array<LastAddress> | undefined;
};
export const codecForLastAddress = (): Codec<LastAddress> =>
  buildCodecForObject<LastAddress>()
    .property("address", codecForAny())
    .property("type", codecForString())
    .property("savedAt", codecForAbsoluteTime)
    .build("LastAddress");

export const codecForSessionState = (): Codec<SessionState> =>
  buildCodecForObject<SessionState>()
    .property("completedURL", codecOptional(codecForStringURL()))
    .property("lastAddress", codecOptional(codecForList(codecForLastAddress())))
    .build("SessionState");

export interface SessionStateHandler {
  state: SessionState | undefined;
  start(): void;
  saveAddress(type: string, address: Record<string, string>): void;
  removeAddress(index: number): void;
  sent(info: ChallengerApi.ChallengeCreateResponse): void;
  failed(info: ChallengerApi.InvalidPinResponse): void;
  completed(info: ChallengerApi.ChallengeRedirect): void;
}

const SESSION_STATE_KEY = buildStorageKey(
  "challenger-session",
  codecForSessionState(),
);

/**
 * Return getters and setters for
 * login credentials and backend's
 * base URL.
 */
export function useSessionState(): SessionStateHandler {
  const { value: state, update } = useLocalStorage(SESSION_STATE_KEY);

  return {
    state,
    start() {
      update({
        completedURL: undefined,
        lastAddress: state?.lastAddress ?? [],
      });
    },
    removeAddress(index) {
      const lastAddr = [...(state?.lastAddress ?? [])];
      lastAddr.splice(index, 1);
      update({
        completedURL: undefined,
        lastAddress: lastAddr,
      });
    },
    saveAddress(type, address) {
      const lastAddr = [...(state?.lastAddress ?? [])];
      lastAddr.push({
        type,
        address: address ?? {},
        savedAt: AbsoluteTime.now(),
      });
      update({
        completedURL: undefined,
        lastAddress: lastAddr,
      });
    },
    sent(info) {
    },
    failed(info) {
    },
    completed(info) {
      if (!state) {
        update({
          completedURL: info.redirect_url,
          lastAddress: [],
        });
      } else {
        update({
          completedURL: info.redirect_url,
          lastAddress: state.lastAddress,
        });
      }
    },
  };
}
