import type { DefaultError, WithRequired } from '@tanstack/query-core'
import type { CreateMutationOptions } from './types'

/**
 * You can generally pass everything to `mutationOptions` that you can also pass to `injectMutation`. A
 * `mutationKey` is required on this overload so the mutation can be looked up later, e.g. with
 * `injectMutationState`.
 * @param options - The mutation options to use, identical to what you'd pass to `injectMutation`, with a
 * required `mutationKey`.
 * @returns The same options object, unchanged.
 * @see {@link injectMutation} to run the mutation these options describe.
 * @example
 * Looking the mutation up elsewhere via its `mutationKey`, e.g. for a global "saving…" indicator:
 * ```angular-ts
 * import { mutationOptions, injectMutationState } from '@tanstack/angular-query-experimental'
 *
 * const createPostOptions = mutationOptions({
 *   mutationKey: ['posts', 'create'],
 *   mutationFn: createPost,
 * })
 *
 * @Component({
 *   selector: 'saving-indicator',
 *   template: `
 *     @if (isCreatingPost()) {
 *       <span>Saving…</span>
 *     }
 *   `,
 * })
 * export class SavingIndicator {
 *   readonly #pendingCreates = injectMutationState(() => ({
 *     filters: { mutationKey: createPostOptions.mutationKey, status: 'pending' },
 *   }))
 *   readonly isCreatingPost = computed(() => this.#pendingCreates().length > 0)
 * }
 * ```
 */
export function mutationOptions<
  TData = unknown,
  TError = DefaultError,
  TVariables = void,
  TOnMutateResult = unknown,
>(
  options: WithRequired<
    CreateMutationOptions<TData, TError, TVariables, TOnMutateResult>,
    'mutationKey'
  >,
): WithRequired<
  CreateMutationOptions<TData, TError, TVariables, TOnMutateResult>,
  'mutationKey'
>
/**
 * You can generally pass everything to `mutationOptions` that you can also pass to `injectMutation`. No
 * `mutationKey` is required on this overload — use this when you don't need to target the mutation via a
 * `mutationKey` filter later (e.g. with `injectMutationState`); it can still be observed through other
 * filters, such as `status`.
 * @param options - The mutation options to use, identical to what you'd pass to `injectMutation`, without a
 * `mutationKey`.
 * @returns The same options object, unchanged.
 * @remarks See the other overload's example for looking a mutation up via `injectMutationState`.
 * @see {@link injectMutation} to run the mutation these options describe.
 * @example
 * Sharing options across services, so `QueriesService` stays the single place a mutation is defined:
 * ```angular-ts
 * import { mutationOptions, injectMutation } from '@tanstack/angular-query-experimental'
 *
 * @Injectable({ providedIn: 'root' })
 * export class QueriesService {
 *   readonly #queryClient = inject(QueryClient)
 *
 *   updatePost(id: number) {
 *     return mutationOptions({
 *       mutationFn: (post: Partial<Post>) => putPost(id, post),
 *       onSuccess: (newPost) => this.#queryClient.setQueryData(['posts', id], newPost),
 *     })
 *   }
 * }
 *
 * @Component({
 *   selector: 'post',
 *   template: `<button (click)="save()">Save</button>`,
 * })
 * export class Post {
 *   readonly queries = inject(QueriesService)
 *   readonly id = signal(0)
 *   readonly updatePostMutation = injectMutation(() => this.queries.updatePost(this.id()))
 *
 *   save() {
 *     this.updatePostMutation.mutate({ title: 'New Title' })
 *   }
 * }
 * ```
 */
export function mutationOptions<
  TData = unknown,
  TError = DefaultError,
  TVariables = void,
  TOnMutateResult = unknown,
>(
  options: Omit<
    CreateMutationOptions<TData, TError, TVariables, TOnMutateResult>,
    'mutationKey'
  >,
): Omit<
  CreateMutationOptions<TData, TError, TVariables, TOnMutateResult>,
  'mutationKey'
>
export function mutationOptions<
  TData = unknown,
  TError = DefaultError,
  TVariables = void,
  TOnMutateResult = unknown,
>(
  options: CreateMutationOptions<TData, TError, TVariables, TOnMutateResult>,
): CreateMutationOptions<TData, TError, TVariables, TOnMutateResult> {
  return options
}