/*
 This file is part of GNU Taler
 (C) 2021-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/>
 */

/**
 *
 * @author Sebastian Javier Marchano (sebasjm)
 */

import { MerchantAuthMethod, TalerMerchantApi } from "@gnu-taler/taler-util";
import { useMerchantApiContext } from "@gnu-taler/web-util/browser";
import * as tests from "@gnu-taler/web-util/testing";
import { expect } from "chai";
import { useBackendInstances, useInstanceDetails } from "./instance.js";
import { ApiMockEnvironment } from "./testing.js";
import {
  API_CREATE_INSTANCE,
  API_DELETE_INSTANCE,
  API_GET_CURRENT_INSTANCE,
  API_LIST_INSTANCES,
  API_UPDATE_CURRENT_INSTANCE,
  API_UPDATE_CURRENT_INSTANCE_AUTH,
  API_UPDATE_INSTANCE_BY_ID,
} from "./urls.js";

describe("instance api interaction with details", () => {
  it("should evict cache when updating an instance", async () => {
    const env = new ApiMockEnvironment();

    env.addRequestExpectation(API_GET_CURRENT_INSTANCE, {
      response: {
        name: "instance_name",
      } as TalerMerchantApi.QueryInstancesResponse,
    });

    const hookBehavior = await tests.hookBehaveLikeThis(
      () => {
        // const api = useInstanceAPI();
        const { lib: api } = useMerchantApiContext();
        const query = useInstanceDetails();
        return { query, api };
      },
      {},
      [
        ({ query, api }) => {
          // expect(query.loading).true;
        },
        ({ query, api }) => {
          expect(env.assertJustExpectedRequestWereMade()).deep.eq({
            result: "ok",
          });

          // expect(query.loading).false;
          // expect(query.ok).true;
          // if (!query.ok) return;
          // expect(query.data).deep.equals({
          //   name: "instance_name",
          // });
          env.addRequestExpectation(API_UPDATE_CURRENT_INSTANCE, {
            request: {
              name: "other_name",
            } as TalerMerchantApi.InstanceReconfigurationMessage,
          });
          env.addRequestExpectation(API_GET_CURRENT_INSTANCE, {
            response: {
              name: "other_name",
            } as TalerMerchantApi.QueryInstancesResponse,
          });
          api.instance.updateCurrentInstance(undefined, {
            name: "other_name",
          } as TalerMerchantApi.InstanceReconfigurationMessage);
        },
        ({ query, api }) => {
          expect(env.assertJustExpectedRequestWereMade()).deep.eq({
            result: "ok",
          });
          // expect(query.loading).false;
          // expect(query.ok).true;
          // if (!query.ok) return;
          // expect(query.data).deep.equals({
          //   name: "other_name",
          // });
        },
      ],
      env.buildTestingContext(),
    );

    expect(hookBehavior).deep.eq({ result: "ok" });
    expect(env.assertJustExpectedRequestWereMade()).deep.eq({ result: "ok" });
  });

  it("should evict cache when setting the instance's token", async () => {
    const env = new ApiMockEnvironment();

    // env.addRequestExpectation(API_GET_CURRENT_INSTANCE, {
    //   response: {
    //     name: "instance_name",
    //     auth: {
    //       method: "token",
    //       // token: "not-secret",
    //     },
    //   } as TalerMerchantApi.QueryInstancesResponse,
    // });

    const hookBehavior = await tests.hookBehaveLikeThis(
      () => {
        const { lib: api } = useMerchantApiContext();
        const query = useInstanceDetails();
        return { query, api };
      },
      {},
      [
        ({ query, api }) => {
          // expect(query.loading).true;
        },
        ({ query, api }) => {
          expect(env.assertJustExpectedRequestWereMade()).deep.eq({
            result: "ok",
          });
          // expect(query.loading).false;
          // expect(query.ok).true;
          // if (!query.ok) return;
          // expect(query.data).deep.equals({
          //   name: "instance_name",
          //   auth: {
          //     method: "token",
          //   },
          // });
          env.addRequestExpectation(API_UPDATE_CURRENT_INSTANCE_AUTH, {
            request: {
              method: "token",
              password: "secret",
            } as TalerMerchantApi.InstanceAuthConfigurationMessage,
          });
          // env.addRequestExpectation(API_NEW_LOGIN, {
          //   auth: "secret",
          //   request: {
          //     scope: "write",
          //     duration: {
          //       "d_us": "forever",
          //     },
          //     refreshable: true,
          //   },
          // });
          env.addRequestExpectation(API_GET_CURRENT_INSTANCE, {
            response: {
              name: "instance_name",
              auth: {
                method: "token",
                // token: "secret",
              },
            } as TalerMerchantApi.QueryInstancesResponse,
          });
          // api.setNewAccessToken(undefined, "secret" as AccessToken);
        },
        ({ query, api }) => {
          expect(env.assertJustExpectedRequestWereMade()).deep.eq({
            result: "ok",
          });
          // expect(query.loading).false;
          // expect(query.ok).true;
          // if (!query.ok) return;
          // expect(query.data).deep.equals({
          //   name: "instance_name",
          //   auth: {
          //     method: "token",
          //     // token: "secret",
          //   },
          // });
        },
      ],
      env.buildTestingContext(),
    );
    expect(hookBehavior).deep.eq({ result: "ok" });
    expect(env.assertJustExpectedRequestWereMade()).deep.eq({ result: "ok" });
  });

  it("should evict cache when clearing the instance's token", async () => {
    const env = new ApiMockEnvironment();

    env.addRequestExpectation(API_GET_CURRENT_INSTANCE, {
      response: {
        name: "instance_name",
        auth: {
          method: "token",
          // token: "not-secret",
        },
      } as TalerMerchantApi.QueryInstancesResponse,
    });

    const hookBehavior = await tests.hookBehaveLikeThis(
      () => {
        const { lib: api } = useMerchantApiContext();
        const query = useInstanceDetails();
        return { query, api };
      },
      {},
      [
        ({ query, api }) => {
          // expect(query.loading).true;
        },
        ({ query, api }) => {
          expect(env.assertJustExpectedRequestWereMade()).deep.eq({
            result: "ok",
          });
          // expect(query.loading).false;
          // expect(query.ok).true;
          // if (!query.ok) return;
          // expect(query.data).deep.equals({
          //   name: "instance_name",
          //   auth: {
          //     method: "token",
          //     // token: "not-secret",
          //   },
          // });
          env.addRequestExpectation(API_UPDATE_CURRENT_INSTANCE_AUTH, {
            request: {
              method: "token",
              password: "asd"
            } as TalerMerchantApi.InstanceAuthConfigurationMessage,
          });
          env.addRequestExpectation(API_GET_CURRENT_INSTANCE, {
            response: {
              name: "instance_name",
              // auth: {
              //   method: "token",
              //   password: "asd"
              // }
            } as TalerMerchantApi.QueryInstancesResponse,
          });

          api.instance.updateCurrentInstanceAuthentication(undefined, {
            method: MerchantAuthMethod.TOKEN,
            password: "asd"
          });
        },
        ({ query, api }) => {
          expect(env.assertJustExpectedRequestWereMade()).deep.eq({
            result: "ok",
          });
          // expect(query.loading).false;
          // expect(query.ok).true;
          // if (!query.ok) return;
          // expect(query.data).deep.equals({
          //   name: "instance_name",
          //   auth: {
          //     method: "external",
          //   },
          // });
        },
      ],
      env.buildTestingContext(),
    );
    expect(hookBehavior).deep.eq({ result: "ok" });
    expect(env.assertJustExpectedRequestWereMade()).deep.eq({ result: "ok" });
    // const { result, waitForNextUpdate } = renderHook(
    //   () => {
    //     const api = useInstanceAPI();
    //     const query = useInstanceDetails();

    //     return { query, api };
    //   },
    //   { wrapper: TestingContext }
    // );

    // expect(result.current).not.undefined;
    // if (!result.current) {
    //   return;
    // }
    // expect(result.current.query.loading).true;

    // await waitForNextUpdate({ timeout: 1 });

    // expect(env.assertJustExpectedRequestWereMade()).deep.eq({ result: "ok" });

    // expect(result.current.query.loading).false;

    // expect(result.current?.query.ok).true;
    // if (!result.current?.query.ok) return;

    // expect(result.current.query.data).equals({
    //   name: 'instance_name',
    //   auth: {
    //     method: 'token',
    //     token: 'not-secret',
    //   }
    // });

    // act(async () => {
    //   await result.current?.api.clearToken();
    // });

    // expect(env.assertJustExpectedRequestWereMade()).deep.eq({ result: "ok" });

    // expect(result.current.query.loading).false;

    // await waitForNextUpdate({ timeout: 1 });

    // expect(env.assertJustExpectedRequestWereMade()).deep.eq({ result: "ok" });

    // expect(result.current.query.loading).false;
    // expect(result.current.query.ok).true;

    // expect(result.current.query.data).equals({
    //   name: 'instance_name',
    //   auth: {
    //     method: 'external',
    //   }
    // });
  });
});

describe("instance admin api interaction with listing", () => {
  it("should evict cache when creating a new instance", async () => {
    const env = new ApiMockEnvironment();

    env.addRequestExpectation(API_LIST_INSTANCES, {
      response: {
        instances: [
          {
            name: "instance_name",
          } as TalerMerchantApi.Instance,
        ],
      },
    });

    const hookBehavior = await tests.hookBehaveLikeThis(
      () => {
        const { lib: api } = useMerchantApiContext();
        const query = useBackendInstances();
        return { query, api };
      },
      {},
      [
        ({ query, api }) => {
          // expect(query.loading).true;
        },
        ({ query, api }) => {
          expect(env.assertJustExpectedRequestWereMade()).deep.eq({
            result: "ok",
          });
          // expect(query.loading).false;
          // expect(query.ok).true;
          // if (!query.ok) return;
          // expect(query.data).deep.equals({
          //   instances: [
          //     {
          //       name: "instance_name",
          //     },
          //   ],
          // });

          env.addRequestExpectation(API_CREATE_INSTANCE, {
            request: {
              name: "other_name",
            } as TalerMerchantApi.InstanceConfigurationMessage,
          });
          env.addRequestExpectation(API_LIST_INSTANCES, {
            response: {
              instances: [
                {
                  name: "instance_name",
                } as TalerMerchantApi.Instance,
                {
                  name: "other_name",
                } as TalerMerchantApi.Instance,
              ],
            },
          });

          api.instance.createInstance(undefined, {
            name: "other_name",
          } as TalerMerchantApi.InstanceConfigurationMessage);
        },
        ({ query, api }) => {
          expect(env.assertJustExpectedRequestWereMade()).deep.eq({
            result: "ok",
          });
          // expect(query.loading).false;
          // expect(query.ok).true;
          // if (!query.ok) return;
          // expect(query.data).deep.equals({
          //   instances: [
          //     {
          //       name: "instance_name",
          //     },
          //     {
          //       name: "other_name",
          //     },
          //   ],
          // });
        },
      ],
      env.buildTestingContext(),
    );
    expect(hookBehavior).deep.eq({ result: "ok" });
    expect(env.assertJustExpectedRequestWereMade()).deep.eq({ result: "ok" });
  });

  it("should evict cache when deleting an instance", async () => {
    const env = new ApiMockEnvironment();

    env.addRequestExpectation(API_LIST_INSTANCES, {
      response: {
        instances: [
          {
            id: "admin",
            name: "instance_name",
          } as TalerMerchantApi.Instance,
          {
            id: "the_id",
            name: "second_instance",
          } as TalerMerchantApi.Instance,
        ],
      },
    });

    const hookBehavior = await tests.hookBehaveLikeThis(
      () => {
        const { lib: api } = useMerchantApiContext();
        const query = useBackendInstances();
        return { query, api };
      },
      {},
      [
        ({ query, api }) => {
          // expect(query.loading).true;
        },
        ({ query, api }) => {
          expect(env.assertJustExpectedRequestWereMade()).deep.eq({
            result: "ok",
          });
          // expect(query.loading).false;
          // expect(query.ok).true;
          // if (!query.ok) return;
          // expect(query.data).deep.equals({
          //   instances: [
          //     {
          //       id: "admin",
          //       name: "instance_name",
          //     },
          //     {
          //       id: "the_id",
          //       name: "second_instance",
          //     },
          //   ],
          // });

          env.addRequestExpectation(API_DELETE_INSTANCE("the_id"), {});
          env.addRequestExpectation(API_LIST_INSTANCES, {
            response: {
              instances: [
                {
                  id: "admin",
                  name: "instance_name",
                } as TalerMerchantApi.Instance,
              ],
            },
          });

          api.instance.deleteInstance(undefined, "the_id");
        },
        ({ query, api }) => {
          expect(env.assertJustExpectedRequestWereMade()).deep.eq({
            result: "ok",
          });
          // expect(query.loading).false;
          // expect(query.ok).true;
          // if (!query.ok) return;
          // expect(query.data).deep.equals({
          //   instances: [
          //     {
          //       id: "admin",
          //       name: "instance_name",
          //     },
          //   ],
          // });
        },
      ],
      env.buildTestingContext(),
    );
    expect(hookBehavior).deep.eq({ result: "ok" });
    expect(env.assertJustExpectedRequestWereMade()).deep.eq({ result: "ok" });

    // const { result, waitForNextUpdate } = renderHook(
    //   () => {
    //     const api = useAdminAPI();
    //     const query = useBackendInstances();

    //     return { query, api };
    //   },
    //   { wrapper: TestingContext }
    // );

    // expect(result.current).not.undefined;
    // if (!result.current) {
    //   return;
    // }
    // expect(result.current.query.loading).true;

    // await waitForNextUpdate({ timeout: 1 });

    // expect(env.assertJustExpectedRequestWereMade()).deep.eq({ result: "ok" });

    // expect(result.current.query.loading).false;

    // expect(result.current?.query.ok).true;
    // if (!result.current?.query.ok) return;

    // expect(result.current.query.data).equals({
    //   instances: [{
    //     id: 'admin',
    //     name: 'instance_name'
    //   }, {
    //     id: 'the_id',
    //     name: 'second_instance'
    //   }]
    // });

    // env.addRequestExpectation(API_DELETE_INSTANCE('the_id'), {});

    // act(async () => {
    //   await result.current?.api.deleteInstance('the_id');
    // });

    // expect(env.assertJustExpectedRequestWereMade()).deep.eq({ result: "ok" });

    // env.addRequestExpectation(API_LIST_INSTANCES, {
    //   response: {
    //     instances: [{
    //       id: 'admin',
    //       name: 'instance_name'
    //     } as TalerMerchantApi.Instance]
    //   },
    // });

    // expect(result.current.query.loading).false;

    // await waitForNextUpdate({ timeout: 1 });

    // expect(env.assertJustExpectedRequestWereMade()).deep.eq({ result: "ok" });

    // expect(result.current.query.loading).false;
    // expect(result.current.query.ok).true;

    // expect(result.current.query.data).equals({
    //   instances: [{
    //     id: 'admin',
    //     name: 'instance_name'
    //   }]
    // });
  });

  it("should evict cache when deleting (purge) an instance", async () => {
    const env = new ApiMockEnvironment();

    env.addRequestExpectation(API_LIST_INSTANCES, {
      response: {
        instances: [
          {
            id: "admin",
            name: "instance_name",
          } as TalerMerchantApi.Instance,
          {
            id: "the_id",
            name: "second_instance",
          } as TalerMerchantApi.Instance,
        ],
      },
    });

    const hookBehavior = await tests.hookBehaveLikeThis(
      () => {
        const { lib: api } = useMerchantApiContext();
        const query = useBackendInstances();
        return { query, api };
      },
      {},
      [
        ({ query, api }) => {
          // expect(query.loading).true;
        },
        ({ query, api }) => {
          expect(env.assertJustExpectedRequestWereMade()).deep.eq({
            result: "ok",
          });
          // expect(query.loading).false;
          // expect(query.ok).true;
          // if (!query.ok) return;
          // expect(query.data).deep.equals({
          //   instances: [
          //     {
          //       id: "admin",
          //       name: "instance_name",
          //     },
          //     {
          //       id: "the_id",
          //       name: "second_instance",
          //     },
          //   ],
          // });

          env.addRequestExpectation(API_DELETE_INSTANCE("the_id"), {
            qparam: {
              purge: "YES",
            },
          });
          env.addRequestExpectation(API_LIST_INSTANCES, {
            response: {
              instances: [
                {
                  id: "admin",
                  name: "instance_name",
                } as TalerMerchantApi.Instance,
              ],
            },
          });

          api.instance.deleteInstance(undefined, "the_id", { purge: true });
        },
        ({ query, api }) => {
          expect(env.assertJustExpectedRequestWereMade()).deep.eq({
            result: "ok",
          });
          // expect(query.loading).false;
          // expect(query.ok).true;
          // if (!query.ok) return;
          // expect(query.data).deep.equals({
          //   instances: [
          //     {
          //       id: "admin",
          //       name: "instance_name",
          //     },
          //   ],
          // });
        },
      ],
      env.buildTestingContext(),
    );
    expect(hookBehavior).deep.eq({ result: "ok" });
    expect(env.assertJustExpectedRequestWereMade()).deep.eq({ result: "ok" });
  });
});

describe("instance management api interaction with listing", () => {
  it("should evict cache when updating an instance", async () => {
    const env = new ApiMockEnvironment();

    env.addRequestExpectation(API_LIST_INSTANCES, {
      response: {
        instances: [
          {
            id: "managed",
            name: "instance_name",
          } as TalerMerchantApi.Instance,
        ],
      },
    });

    const hookBehavior = await tests.hookBehaveLikeThis(
      () => {
        const { lib: api } = useMerchantApiContext();
        const query = useBackendInstances();
        return { query, api };
      },
      {},
      [
        ({ query, api }) => {
          // expect(query.loading).true;
        },
        ({ query, api }) => {
          expect(env.assertJustExpectedRequestWereMade()).deep.eq({
            result: "ok",
          });
          // expect(query.loading).false;
          // expect(query.ok).true;
          // if (!query.ok) return;
          // expect(query.data).deep.equals({
          //   instances: [
          //     {
          //       id: "managed",
          //       name: "instance_name",
          //     },
          //   ],
          // });

          env.addRequestExpectation(API_UPDATE_INSTANCE_BY_ID("managed"), {
            request: {
              name: "other_name",
            } as TalerMerchantApi.InstanceReconfigurationMessage,
          });
          env.addRequestExpectation(API_LIST_INSTANCES, {
            response: {
              instances: [
                {
                  id: "managed",
                  name: "other_name",
                } as TalerMerchantApi.Instance,
              ],
            },
          });

          api.instance.updateCurrentInstance(undefined, {
            name: "other_name",
          } as TalerMerchantApi.InstanceConfigurationMessage);
        },
        ({ query, api }) => {
          expect(env.assertJustExpectedRequestWereMade()).deep.eq({
            result: "ok",
          });
          // expect(query.loading).false;
          // expect(query.ok).true;
          // if (!query.ok) return;
          // expect(query.data).deep.equals({
          //   instances: [
          //     {
          //       id: "managed",
          //       name: "other_name",
          //     },
          //   ],
          // });
        },
      ],
      env.buildTestingContext(),
    );
    expect(hookBehavior).deep.eq({ result: "ok" });
    expect(env.assertJustExpectedRequestWereMade()).deep.eq({ result: "ok" });
  });
});
