/*
 * Copyright (C) 2023 StApps
 * This program 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, version 3.
 *
 * This program 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
 * this program. If not, see <https://www.gnu.org/licenses/>.
 */

/* eslint-disable @typescript-eslint/no-explicit-any */
import equal from 'fast-deep-equal';
import {extendsDeepEqual} from '@openstapps/collection-utils';
import {CORE_VERSION, SCIndexResponse, SCSearchRequest, SCSearchResponse} from '@openstapps/core';
import * as defaultConfig from '../../fixtures/config/default-config.json';

type InterceptArguments = {
  fixture?: string | SCSearchResponse | ((request: SCSearchRequest) => SCSearchResponse);
  alias?: string;
} & (
  | {
      exact: string | SCSearchRequest | ((searchRequest: SCSearchRequest) => boolean);
    }
  | {
      extends: string | SCSearchRequest;
    }
);

/**
 *
 */
export function interceptConfig(config?: string) {
  let localConfig: SCIndexResponse = defaultConfig as unknown as SCIndexResponse;
  localConfig.backend.SCVersion = CORE_VERSION;
  cy.intercept(
    {url: '/', method: 'POST'},
    config ? {fixture: config} : {body: JSON.stringify(localConfig)},
  ).as('config');
}

/**
 *
 */
export function interceptBackend(route: string, fixtureKey: string, parameters: InterceptArguments) {
  const condition = 'exact' in parameters ? parameters.exact : parameters.extends;
  parameters.fixture ||= 'no-results';

  Cypress.Promise.all([
    typeof condition === 'string' ? cy.fixture(`${condition}.${fixtureKey}.req.json`) : condition,
    typeof parameters.fixture === 'string'
      ? cy.fixture(`${parameters.fixture}.${fixtureKey}.res.json`)
      : parameters.fixture,
    'exact' in parameters ? equal : extendsDeepEqual,
  ] as const).spread((requestCondition, response, comparisonFunction) => {
    cy.intercept({url: route, method: 'POST'}, request => {
      const body = request.body;

      if (
        typeof requestCondition === 'object'
          ? (comparisonFunction as any)(requestCondition, body)
          : (requestCondition as any)(body)
      ) {
        request.alias = parameters.alias;

        request.reply(typeof response === 'object' ? response : (response as any)(body));
      }
    });
  });
}

/**
 *
 */
export function interceptSearch(parameters: InterceptArguments) {
  parameters.alias ||= 'search';
  cy.interceptBackend('/search', 'search', parameters);
}

/**
 *
 */
export function interceptMultiSearch(parameters: InterceptArguments) {
  parameters.alias ||= 'multi-search';
  cy.interceptBackend('/search/multi', 'multi', parameters);
}

/**
 *
 */
export function interceptGet(parameters: Omit<InterceptArguments, 'exact' | 'extends'> & {uid: string}) {
  cy.interceptBackend('/search', 'get', {
    exact: request => {
      return (
        request.size === 1 &&
        request.filter?.type === 'value' &&
        request.filter.arguments.field === 'uid' &&
        request.filter.arguments.value === parameters.uid
      );
    },
    fixture: parameters.fixture || `get/${parameters.uid}`,
    alias: parameters.alias || parameters.uid,
  });
}
