import type { Observable, ObservableInput, OperatorFunction } from 'rxjs';
import type { QueryCacheRecord } from './cache';
import type { QueryClientOptions, QueryClientResult, RetryOptions } from './client';
import type { QueryTask } from './QueryTask';

/**
 * Representation of item in the query queue.
 * @template TArgs The type of the arguments passed to the query function. Defaults to unknown.
 */
export type QueryQueueItem<TArgs> = {
  /**
   * A unique identifier for the task to be executed.
   */
  task: string;
  /**
   * The arguments to be passed to the task.
   */
  args: TArgs;
  /**
   * The timestamp when the item was created.
   */
  created: number;
  /**
   * Optional configurations for the query client.
   */
  options?: Partial<QueryClientOptions>;
};

/**
 * Represents a query result with its status, transaction details, arguments, and value.
 * @template TValue The type of the value returned by the query. Defaults to unknown.
 */
export type QueryTaskValue<TValue = unknown> = {
  /**
   * A unique key that identifies the task.
   */
  key: string;
  /**
   * The current status of the task.
   */
  status: string;
  /**
   * A unique identifier for the transaction.
   */
  transaction: string;
  /**
   * The timestamp when the task was created.
   */
  created: number;
  /**
   * The value returned by the task.
   */
  value: TValue;
};

/**
 * Represents a query result which is completed
 * @template TValue The type of the value returned by the query. Defaults to unknown.
 */
export type QueryTaskCompleted<TValue> = QueryTaskValue<TValue> & {
  /**
   * A unique identifier for the completed task.
   */
  uuid: string;
  /**
   * The status of the task, which is always 'complete' for this type.
   */
  status: 'complete';
  /**
   * The timestamp when the task was completed.
   */
  complete: number;
};

/**
 * Represents a query result which is cached
 * @template TValue The type of the value returned by the query. Defaults to unknown.
 */
export type QueryTaskCached<TValue> = Omit<QueryTaskValue<TValue>, 'uuid'> & {
  /**
   * The status of the task, which is always 'cache' for this type.
   */
  status: 'cache';
  /**
   * The timestamp when the task was last updated.
   */
  updated?: number;
  /**
   * The number of times the task has been updated.
   */
  updates?: number;
  /**
   * The timestamp when the task was last mutated.
   */
  mutated?: number;

  /**
   * flag if the cache was valid
   */
  hasValidCache?: boolean;
};

/**
 * Represents of a completed query task
 * Result after a query task has been completed.
 * @template TValue The type of the value returned by the query. Defaults to unknown.
 */
export type QueryQueueResult<TValue = unknown, TArgs = unknown> = {
  /**
   * The task that was executed.
   */
  task: QueryTask<TValue, TArgs>;
  /**
   * The result of executing the task.
   */
  result: QueryClientResult<TValue, TArgs>;
};

/**
 * Cache validator function.
 * @template TType The type of the query result.
 * @returns A boolean value indicating whether the cache entry is still valid.
 */
export type CacheValidator<TType, TArgs> = (
  /**
   * The cache record to be validated.
   */
  entry: QueryCacheRecord<TType, TArgs>,
  /**
   * The arguments used for validation.
   */
  args: TArgs,
) => boolean;

/**
 * Cache options for a query.
 * @template TType The type of the query result.
 * @template TArgs The type of the query arguments.
 */
export type CacheOptions<TType, TArgs> = {
  /**
   * A function to generate a unique key for caching based on the query arguments.
   */
  key: (query: TArgs) => string;

  /**
   * A function to validate if a cache entry is still valid.
   */
  validate: CacheValidator<TType, TArgs>;
};

/**
 * Query options for a query.
 * @template TType The type of the query result.
 * @template TArgs The type of the query arguments.
 */
export type QueryOptions<TType, TArgs = unknown> = {
  /**
   * An optional AbortSignal to cancel the request.
   */
  signal?: AbortSignal;
  /**
   * Configuration for retry behavior.
   */
  retry?: Partial<RetryOptions>;
  /**
   * Cache options.
   */
  cache?: {
    /**
     * Whether to suppress errors when a cache entry is invalid.
     */
    suppressInvalid: boolean;
    /**
     * An optional function to validate cache entries.
     */
    validate?: CacheValidator<TType, TArgs>;
  };
};

/**
 * Options for retrying a query.
 */
export type RetryOpt = {
  /**
   * The maximum number of retry attempts.
   */
  count: number;
  /**
   * The delay between retries, either as a fixed number of milliseconds or a function that returns an ObservableInput.
   */
  delay: number | ((error: unknown) => ObservableInput<void>);
};

/**
 * Callback function for a query.
 * @template TType The type of the query result.
 * @template TArgs The type of the query arguments.
 */
export type QueryFn<TType, TArgs> = (
  /**
   * The arguments for the query function.
   */
  args: TArgs,
  /**
   * An optional AbortSignal to cancel the query.
   */
  signal?: AbortSignal,
) => ObservableInput<TType>;

/**
 * Queue operator function for queries.
 * Operator function for controlling the processing of queries in a queue.
 * @template TValue The type of the value returned by the query. Defaults to unknown.
 * @template TArgs The type of the arguments passed to the query function. Defaults to unknown.
 */
export type QueryQueueFn<TValue = unknown, TArgs = unknown> = (
  /**
   * A function that takes a task identifier and returns an Observable of the query queue result.
   */
  fn: (task: string) => Observable<QueryQueueResult<TValue, TArgs>>,
) => OperatorFunction<string, QueryQueueResult<TValue, TArgs>>;
