/*
 * 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-namespace,@typescript-eslint/no-explicit-any */
import type {Component} from '@angular/core';
import {
  interceptBackend,
  interceptConfig,
  interceptGet,
  interceptMultiSearch,
  interceptSearch,
} from './commands/backend';
import {component, ng, runInsideAngular, zone} from './commands/angular';
import {
  clearAllSettings,
  getAllSettings,
  getSetting,
  setLocalConfig,
  setSettings,
  storage,
} from './commands/settings';
import {patchSearchPage} from './commands/patches';

const commands = {
  interceptConfig,
  interceptBackend,
  interceptSearch,
  interceptMultiSearch,
  interceptGet,
  storage,
  setLocalConfig,
  setSettings,
  getSetting,
  clearAllSettings,
  getAllSettings,
  patchSearchPage,
  ng,
  zone,
};

const childCommands = {
  component,
  runInsideAngular,
};

Cypress.Commands.addAll(commands);
Cypress.Commands.addAll({prevSubject: true}, childCommands);

declare global {
  // eslint-disable-next-line @typescript-eslint/no-namespace
  namespace Cypress {
    // items that include generics also have to be defined here separately
    // eslint-disable-next-line @typescript-eslint/no-empty-interface
    interface Chainable extends CustomCommands, CustomChildCommands {
      component<T = Component>(): Cypress.Chainable<T>;

      runInsideAngular<T = any, U = void>(zoneAwareTask: (subject: T) => U): Cypress.Chainable<U>;
    }
  }
}

type CustomCommands = {
  [KEY in keyof typeof commands]: (
    ...parameters: Parameters<(typeof commands)[KEY]>
  ) => ChainableReturnType<(typeof commands)[KEY]>;
};
type OmitFirstArgument<F> = F extends (x: any, ...arguments_: infer P) => infer R
  ? (...arguments_: P) => R
  : never;
type CustomChildCommands = {
  [KEY in keyof typeof childCommands]: OmitFirstArgument<(typeof childCommands)[KEY]>;
};
type ChainableReturnType<T extends (...arguments_: any) => any> = ReturnType<T> extends Cypress.Chainable
  ? ReturnType<T>
  : Cypress.Chainable<null>;
