import { LocationDescriptor } from 'history';
import { Data } from '../../types/data';
import { FilterValues } from '../Filters';
import { IconName } from '../Icons';
import { SortData } from '../List';
import { PageHeaderJsActionProps } from '../PageHeader/PageHeaderAction/PageHeaderAction.model';
import { ErrorType } from '../models';
import { BulkEditConfig } from './BulkEdit';

/**
 * Item selection can have two modes:
 * * `SINGLE_ITEMS` - if single items were checked by the user. The selected items are available on the `items` property.
 * * `SELECT_ALL` - if the checkbox on the header is selected. The action is supposed to be executed on _all_ items matching the given `filters` settings.
 */
export type ItemSelection<T extends Data> =
  | SingleItemsSelection<T>
  | SelectAllSelection<T>;

interface SingleItemsSelection<T extends Data> {
  mode: 'SINGLE_ITEMS';
  items?: T[];
}

interface SelectAllSelection<T extends Data> {
  mode: 'SELECT_ALL';
  filters: FilterValues<T>;
}

export interface ExplorerBulkAction<T extends Data>
  extends Omit<PageHeaderJsActionProps, 'onClick'> {
  /**
   * Callback to emit when a user clicks on the component
   * @param arg ItemSelection details of the bulk action
   */
  onClick: (
    arg?: ItemSelection<T>,
  ) => Promise<ErrorType | void> | ErrorType | void;
  /**
   * Whether the Explorer should reload the data once the bulk action is completed
   */
  reloadData?: boolean;
  /**
   * Whether or not to show toast notification once the bulk action is started
   * (shown by default)
   */
  showStartedNotification?: boolean;
}

export type PageIdentifier = string | number | undefined;

export interface DataProviderResults<T extends Data> {
  /** Paginated data results */
  data: T[];
  /** Total amount of results */
  totalCount: number;
  /** Determines if requests for more paginated data should continue (default: true)*/
  hasMoreData: boolean;
  /** Amount of results after filters are applied */
  filteredCount?: number;
  /**
   * Additional paging information to be sent.
   * This property will be reset to undefined when a new set of data is requested.
   */
  pagingInformation?: unknown;
}

export interface ExplorerDataProviderConfiguration<T extends Data> {
  /** Information related to pagination */
  pagingInformation: unknown;
  /** Information about the current filter settings that are requested. */
  filters: FilterValues<T>;
  /** Information about the current sort order that is requested. */
  sorting?: SortData<T>;
}

export interface ExplorerDataProviderConnection<T extends Data> {
  /** Use this method to tell the Explorer that an item has changed. */
  change: (itemId: any, patch: Partial<T>) => void;
  /** Use this method to tell the Explorer that an item got removed. */
  remove: (itemId: any) => void;
  /** Use this method to tell the Explorer that an item got added. */
  add: (values: T) => void;
}

export interface ExplorerDataProvider<T extends Data> {
  /**
   * Called when the Explorer requests new data.
   * The details about the request (page, filter, sorting) are sent as parameter to that method.
   */
  loadData: (
    config: ExplorerDataProviderConfiguration<T>,
  ) => Promise<DataProviderResults<T>>;

  connect?: (
    connection: ExplorerDataProviderConnection<T>,
  ) => (() => void) | void;
}

// redeclare forwardRef
declare module 'react' {
  // eslint-disable-next-line @typescript-eslint/ban-types
  function forwardRef<T, P = {}>(
    render: (props: P, ref: React.Ref<T>) => React.ReactElement | null,
  ): (props: P & React.RefAttributes<T>) => React.ReactElement | null;
}

export interface ExplorerStateOptions {
  filters?: boolean;
  sort?: boolean;
}

export interface QuickEditRegistration<T> {
  /** The label of the action. */
  label: string;
  /** Optional built in icon. This prop also accepts an img src. */
  icon?: IconName | string;
  /** Component to render */
  component: JSX.Element;
  /** Function which will be used to generate the `detailsLink` in `QuickEditContext`.
   *
   * If set to `undefined`, `generateItemLink` prop from `Explorer` will be used.
   *
   * If set to `false`, `detailsLink` will not be generated.
   */
  generateDetailsLink?: ((item: T) => LocationDescriptor<unknown>) | false;
}

export interface QuickEditConfig {
  /** The label of the action. ( Default: 'Quick View' ) */
  label?: string;

  /** Optional built in icon. This prop also accepts an img src. ( Default: IconName.QuickEdit ) */
  icon?: IconName | string;
}

export interface BulkEditRegistration<T extends Data> {
  /** The label of the action. */
  label?: string;
  /** Optional built in icon. This prop also accepts an img src. */
  icon?: IconName | string;
  /** Component to render. This will override the component that is generated. */
  component?: JSX.Element;
  /** Bulk Edit Configuration */
  config?: BulkEditConfig;
  /** Function which will be called when save button is clicked */
  saveData: (data: T, items: ItemSelection<T>) => Promise<void>;
}
