import type { LiftedAction, LiftedState } from '@redux-devtools/core';
import type {
  createApi,
  QueryCacheKey,
  QueryStatus,
} from '@reduxjs/toolkit/query';
import type { Action, AnyAction, Dispatch } from '@reduxjs/toolkit';
import type { ComponentType } from 'react';
import { base16Themes } from 'react-base16-styling';
import type { Base16Theme } from 'react-base16-styling';
import type { QueryComparators } from './utils/comparators';
import type { QueryFilters } from './utils/filters';

export enum QueryPreviewTabs {
  data,
  queryinfo,
  apiConfig,
  querySubscriptions,
  queryTags,
  actions,
}

export interface QueryFormValues {
  queryComparator: QueryComparators;
  isAscendingQueryComparatorOrder: boolean;
  searchValue: string;
  isRegexSearch: boolean;
  queryFilter: QueryFilters;
}
export interface RtkQueryMonitorState {
  readonly queryForm: {
    values: QueryFormValues;
  };
  readonly selectedQueryKey: Pick<QueryInfo, 'reducerPath' | 'queryKey'> | null;
  readonly selectedPreviewTab: QueryPreviewTabs;
}

export interface RtkQueryMonitorProps<S, A extends Action<string>>
  extends LiftedState<S, A, RtkQueryMonitorState> {
  dispatch: Dispatch<Action | LiftedAction<S, A, RtkQueryMonitorState>>;
  theme: keyof typeof base16Themes | Base16Theme;
  invertTheme: boolean;
}

export type RtkQueryApiState = ReturnType<
  ReturnType<typeof createApi>['reducer']
>;

export type RtkQueryState = NonNullable<
  RtkQueryApiState['queries'][keyof RtkQueryApiState]
>;

export type RtkMutationState = NonNullable<
  RtkQueryApiState['mutations'][keyof RtkQueryApiState]
>;

export type RtkQueryApiConfig = RtkQueryApiState['config'];

export type FullTagDescription<TagType> = {
  type: TagType;
  id?: number | string;
};

// This is the actual tags structure, and was the entire `api.provided`
// field up through 2.6.1
export type RtkQueryProvidedTagsState = {
  [x: string]: {
    [id: string]: QueryCacheKey[];
    [id: number]: QueryCacheKey[];
  };
};

// As of 2.6.2, the `api.provided` field is split into `tags` and `keys` fields,
// with the old data nested in `tags`.
export type RtkQuery262ProvidedState = {
  keys: Record<QueryCacheKey, FullTagDescription<any>[]>;
  tags: RtkQueryProvidedTagsState;
};

export function isRtkQuery262Provided(
  provided: Record<string, unknown>,
): provided is RtkQuery262ProvidedState {
  return (
    'tags' in provided &&
    'keys' in provided &&
    typeof provided.tags === 'object' &&
    typeof provided.keys === 'object'
  );
}

export interface ExternalProps<S, A extends Action<string>> {
  dispatch: Dispatch<Action | LiftedAction<S, A, RtkQueryMonitorState>>;
  theme: keyof typeof base16Themes | Base16Theme;
  hideMainButtons?: boolean;
  invertTheme: boolean;
}

export interface QueryInfo {
  type: 'query';
  state: RtkQueryState;
  queryKey: string;
  reducerPath: string;
}

export interface MutationInfo {
  type: 'mutation';
  state: RtkMutationState;
  queryKey: string;
  reducerPath: string;
}

export type RtkResourceInfo = QueryInfo | MutationInfo;

export interface ApiInfo {
  reducerPath: string;
  apiState: RtkQueryApiState;
}

export interface SelectOption<
  T = string,
  VisConfig extends string = 'default',
> {
  label: string;
  value: T;
  visible?: Record<VisConfig | 'default', boolean> | boolean;
}

export interface SelectorsSource<S> {
  userState: S | null;
  monitorState: RtkQueryMonitorState;
  currentStateIndex: number;
  actionsById: LiftedState<unknown, AnyAction, unknown>['actionsById'];
}

export interface StyleUtils {
  readonly base16Theme: Base16Theme;
  readonly invertTheme: boolean;
}

export type RTKQuerySubscribers = NonNullable<
  RtkQueryApiState['subscriptions'][keyof RtkQueryApiState['subscriptions']]
>;

export interface RtkQueryTag {
  type: string;
  id?: number | string;
}

interface Tally {
  count: number;
}

export type QueryTally = {
  [key in QueryStatus]?: number;
} & Tally;

export interface RtkRequestTiming {
  requestId: string;
  queryKey: string;
  endpointName: string;
  startedAt: string;
  completedAt: string;
  duration: string;
}

export interface QueryTimings {
  readonly oldest: RtkRequestTiming | null;
  readonly latest: RtkRequestTiming | null;
  readonly slowest: RtkRequestTiming | null;
  readonly fastest: RtkRequestTiming | null;
  readonly average: string;
  readonly median: string;
}

export interface ApiTimings {
  readonly queries: QueryTimings;
  readonly mutations: QueryTimings;
}

export interface ApiStats {
  readonly timings: ApiTimings;
  readonly tally: Readonly<{
    subscriptions: number;
    cachedQueries: QueryTally;
    tagTypes: number;
    cachedMutations: QueryTally;
  }>;
}

export interface TabOption<S, P, V extends string = 'default'>
  extends SelectOption<S, V> {
  component: ComponentType<P>;
}

/**
 * It is Omit<RequestStatusFlags, 'status'> & { isFetching: boolean; }
 */
export interface RTKStatusFlags {
  readonly isUninitialized: boolean;
  readonly isFetching: boolean;
  readonly isSuccess: boolean;
  readonly isError: boolean;
}

export type RtkRequest = {
  status: QueryStatus;
  queryKey: string;
  requestId: string;
  endpointName: string;
  startedTimeStamp?: number;
  fulfilledTimeStamp?: number;
};
