/*
 This file is part of GNU Taler
 (C) 2022 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 
 */
import { createElement, VNode } from "preact";
function getJsonIfOk(r: Response): Promise {
  if (r.ok) {
    return r.json();
  }
  if (r.status >= 400 && r.status < 500) {
    throw new Error(`URL may not be right: (${r.status}) ${r.statusText}`);
  }
  throw new Error(
    `Try another server: (${r.status}) ${
      r.statusText || "internal server error"
    }`,
  );
}
export async function queryToSlashConfig(url: string): Promise {
  return fetch(new URL("config", url).href)
    .catch(() => {
      throw new Error(`Network error`);
    })
    .then(getJsonIfOk);
}
function timeout(ms: number, promise: Promise): Promise {
  return new Promise((resolve, reject) => {
    const timer = setTimeout(() => {
      reject(
        new Error(
          `Timeout: the query took longer than ${Math.floor(ms / 1000)} secs`,
        ),
      );
    }, ms);
    promise
      .then((value) => {
        clearTimeout(timer);
        resolve(value);
      })
      .catch((reason) => {
        clearTimeout(timer);
        reject(reason);
      });
  });
}
export async function queryToSlashKeys(url: string): Promise {
  const endpoint = new URL("keys", url);
  const query = fetch(endpoint.href)
    .catch(() => {
      throw new Error(`Network error`);
    })
    .then(getJsonIfOk);
  return timeout(3000, query);
}
export type StateFunc = (p: S) => VNode | null;
export type StateViewMap = {
  [S in StateType as S["status"]]: StateFunc;
};
export type RecursiveState = S | (() => RecursiveState);
export function compose(
  name: string,
  hook: (p: PType) => RecursiveState,
  viewMap: StateViewMap,
): (p: PType) => VNode {
  function withHook(stateHook: () => RecursiveState): () => VNode {
    function TheComponent(): VNode {
      const state = stateHook();
      if (typeof state === "function") {
        const subComponent = withHook(state);
        return createElement(subComponent, {});
      }
      const statusName = state.status as unknown as SType["status"];
      const viewComponent = viewMap[statusName] as unknown as StateFunc;
      return createElement(viewComponent, state);
    }
    // TheComponent.name = `${name}`;
    return TheComponent;
  }
  return (p: PType) => {
    const h = withHook(() => hook(p));
    return h();
  };
}
export function assertUnreachable(x: never): never {
  throw new Error("Didn't expect to get here");
}