import { useCallback, useRef, useState } from 'react'
import { cacheInvalidatePrefix } from './http_cache'
import type { MutationState } from '../../types/core/mutation_state'

export interface UseMutationOptions<TData, TVariables> {
  /** Funcion que ejecuta la mutacion (POST/PUT/DELETE) */
  mutation_fn: (variables: TVariables) => Promise<TData>
  /** Callback antes de la mutacion — permite optimistic updates. Retorna contexto de rollback. */
  on_mutate?: (variables: TVariables) => Promise<unknown> | unknown
  /** Callback al exito */
  on_success?: (data: TData, variables: TVariables, context: unknown) => void
  /** Callback al error */
  on_error?: (error: Error, variables: TVariables, context: unknown) => void
  /** Callback siempre (exito o error) */
  on_settled?: (data: TData | undefined, error: Error | null, variables: TVariables, context: unknown) => void
  /** Prefijos de cache a invalidar en exito (ej: ['users'] invalida users, users:list, etc.) */
  invalidate_cache?: string[]
  /** Numero de reintentos ante error (default 0) */
  retry?: number
  /** Delay en ms entre reintentos (default 1000) */
  retry_delay?: number
}

/**
 * Hook para operaciones de escritura POST/PUT/DELETE.
 * No ejecuta al montar. Expone mutate() y mutate_async().
 * Soporta optimistic updates via on_mutate y invalidacion de cache.
 */
export function useMutation<TData, TVariables = void>(
  opts: UseMutationOptions<TData, TVariables>,
): MutationState<TData, TVariables> {
  const {
    mutation_fn,
    on_mutate,
    on_success,
    on_error,
    on_settled,
    invalidate_cache = [],
    retry = 0,
    retry_delay = 1000,
  } = opts

  type MutationInternalState = {
    status: 'idle' | 'loading' | 'success' | 'error'
    data: TData | null
    error: Error | null
  }

  const [state, setState] = useState<MutationInternalState>({ status: 'idle', data: null, error: null })

  const retryCountRef = useRef(0)

  const execute = useCallback(
    async (variables: TVariables): Promise<TData> => {
      setState((s: MutationInternalState) => ({ ...s, status: 'loading', error: null }))

      let context: unknown

      try {
        if (on_mutate) {
          context = await on_mutate(variables)
        }

        const data = await mutation_fn(variables)

        retryCountRef.current = 0
        setState({ status: 'success', data, error: null })

        // Invalidar cache en exito
        for (const prefix of invalidate_cache) {
          cacheInvalidatePrefix(prefix)
        }

        on_success?.(data, variables, context)
        on_settled?.(data, null, variables, context)

        return data
      } catch (err) {
        if (retryCountRef.current < retry) {
          retryCountRef.current += 1
          await new Promise((resolve) => setTimeout(resolve, retry_delay))
          return execute(variables)
        }

        const error = err instanceof Error ? err : new Error(String(err))
        setState((s: MutationInternalState) => ({ ...s, status: 'error', error }))

        on_error?.(error, variables, context)
        on_settled?.(undefined, error, variables, context)

        throw error
      }
    },
    [mutation_fn, on_mutate, on_success, on_error, on_settled, invalidate_cache, retry, retry_delay],
  )

  const mutate = useCallback(
    (variables: TVariables) => {
      execute(variables).catch(() => {})
    },
    [execute],
  )

  const reset = useCallback(() => {
    setState({ status: 'idle', data: null, error: null })
    retryCountRef.current = 0
  }, [])

  return {
    mutate,
    mutate_async: execute,
    data: state.data,
    error: state.error,
    loading: state.status === 'loading',
    reset,
  }
}
