import { whenRequest } from '@wix/fe-essentials/http-client/testkit/client';
import type { AnyScenario } from '@wix/fe-essentials/http-client/testkit';
import {
  GetGlobalSettingsRequest,
  Host,
  SetGlobalSettingsRequest,
} from '@wix/bex-utils/@wix/ambassador-app-settings-v1-settings/types';
import {
  getGlobalSettings,
  setGlobalSettings,
} from '@wix/bex-utils/@wix/ambassador-app-settings-v1-settings/http';
import {
  aGetGlobalSettingsResponse,
  aSetGlobalSettingsResponse,
} from '@wix/bex-utils/@wix/ambassador-app-settings-v1-settings/builders';
import { FiltersMap } from '@wix/bex-core';
import { View } from '../model';

type ViewsServiceTestkitProps = { namespace: string };
type ViewsServiceTestkitParams<T extends FiltersMap> = {
  payload: {
    views?: View<T>[];
    hiddenPresetIds?: string[];
  };
  options?: {
    stub?: jest.Mock<any, any>;
    repeat?: number;
  };
};

export class ViewsServiceTestkit<T extends FiltersMap = {}> {
  private namespace: string;
  private mocks: AnyScenario[] = [];
  private lastRequest: ViewsServiceTestkitParams<T>['payload'] = {
    views: [],
    hiddenPresetIds: [],
  };

  constructor({ namespace }: ViewsServiceTestkitProps) {
    this.namespace = namespace;
  }

  private getNamespace() {
    return `${this.namespace}.views`;
  }

  private shouldPersist(repeat: number) {
    return repeat === 0;
  }

  private addMock(repeat: number = 0, cb: () => AnyScenario) {
    this.shouldPersist(repeat)
      ? this.mocks.push(cb().persist())
      : this.mocks.push(...Array.from(Array(repeat)).map(cb));
  }

  public getMocks() {
    return this.mocks;
  }

  public mockFetchViews({
    payload,
    options = {},
  }: ViewsServiceTestkitParams<T>) {
    const {
      views = this.lastRequest.views || [],
      hiddenPresetIds = this.lastRequest.hiddenPresetIds || [],
    } = payload;

    this.lastRequest = {
      views,
      hiddenPresetIds,
    };

    const { repeat = 0 } = options;
    const shouldPersist = this.shouldPersist(repeat);

    this.addMock(options?.repeat, () =>
      whenRequest(getGlobalSettings)
        .withData(
          (data: GetGlobalSettingsRequest & { __CAIRO_VIEWS_TEST?: string }) =>
            data.__CAIRO_VIEWS_TEST === 'true',
        )
        .reply(() => ({
          status: 200,
          data: aGetGlobalSettingsResponse({
            settings: {
              [this.getNamespace()]: {
                views: shouldPersist ? this.lastRequest.views : views,
                hiddenPresetIds: shouldPersist
                  ? this.lastRequest.hiddenPresetIds
                  : hiddenPresetIds,
              },
            },
          }),
        })),
    );
  }

  public mockUpdateViews({ payload, options }: ViewsServiceTestkitParams<T>) {
    const { views = [] } = payload;
    this.addMock(options?.repeat, () =>
      whenRequest(setGlobalSettings)
        .withData(
          (
            data: SetGlobalSettingsRequest & { __CAIRO_VIEWS_TEST?: string },
          ) => {
            const viewsData = data?.settings?.[this.getNamespace()];
            this.mockFetchViews({ payload: viewsData, options });
            options?.stub?.(viewsData);
            const noViews = !viewsData.views.length && !views.length;

            return (
              data.host === Host.BUSINESS_MANAGER &&
              data.__CAIRO_VIEWS_TEST === 'true' &&
              (noViews ||
                viewsData.views.some((view1: View<T>) =>
                  views.some((view2) => view1.name === view2.name),
                ))
            );
          },
        )
        .reply(() => ({
          status: 200,
          data: aSetGlobalSettingsResponse({
            settings: {
              [this.getNamespace()]: {
                views,
              },
            },
          }),
        })),
    );
  }

  public mockUpdateHiddenPreset({
    payload,
    options,
  }: ViewsServiceTestkitParams<T>) {
    const { hiddenPresetIds = [] } = payload;
    this.addMock(options?.repeat, () =>
      whenRequest(setGlobalSettings)
        .withData(
          (
            data: SetGlobalSettingsRequest & { __CAIRO_VIEWS_TEST?: string },
          ) => {
            this.mockFetchViews({
              payload: { views: [], hiddenPresetIds },
              options,
            });
            options?.stub?.(data?.settings?.[this.getNamespace()]);
            return (
              data.__CAIRO_VIEWS_TEST === 'true' &&
              data?.settings![`${this.namespace}.views`].hiddenPresetIds.some(
                (hiddenPresetId1: string) =>
                  hiddenPresetIds.some(
                    (hiddenPresetId2: string) =>
                      hiddenPresetId1 === hiddenPresetId2,
                  ),
              )
            );
          },
        )
        .reply(() => ({
          status: 200,
          data: aSetGlobalSettingsResponse({
            settings: {
              [this.getNamespace()]: {
                hiddenPresetIds,
              },
            },
          }),
        })),
    );
  }
}
