/**
 * useEditPageSync Hook
 *
 * Handles the common pattern in edit pages:
 * 1. Fetch entity when ID is available
 * 2. Sync form values when entity data changes
 *
 * Reduces ~50 lines of duplicated code across edit pages.
 */

import { useEffect, useState, useCallback } from 'react';
import { useRouter } from 'next/router';
import { AsyncThunk } from '@reduxjs/toolkit';
import { useAppDispatch, useAppSelector } from '../stores/hooks';
import type { RootState } from '../stores/store';

interface UseEditPageSyncOptions<T extends Record<string, unknown>> {
  /** Redux selector to get the entity from state (can return single entity, array, or any slice state value) */
  entitySelector: (state: RootState) => unknown;
  /** Redux async thunk to fetch the entity */
  fetchAction: AsyncThunk<unknown, { id?: string; query?: string }, object>;
  /** Initial form values */
  initialValues: T;
  /** Optional post-processing of entity data before setting form values */
  postProcess?: (entity: T, initial: T) => T;
  /** Optional ID override (defaults to router.query.id) */
  idOverride?: string;
}

interface UseEditPageSyncReturn<T> {
  /** Current form values */
  values: T;
  /** Set form values directly */
  setValues: React.Dispatch<React.SetStateAction<T>>;
  /** Entity ID from router */
  id: string | null;
  /** Whether initial fetch is loading */
  isLoading: boolean;
  /** Whether entity was found */
  isFound: boolean;
}

/**
 * Hook for syncing edit page forms with Redux entity state
 *
 * @example
 * const initVals = { name: '', permissions: [] };
 *
 * const EditRolesPage = () => {
 *   const { values, id, isLoading } = useEditPageSync({
 *     entitySelector: (state) => state.roles.roles,
 *     fetchAction: fetch,
 *     initialValues: initVals,
 *   });
 *
 *   const handleSubmit = async (data) => {
 *     await dispatch(update({ id, data }));
 *     router.push('/roles/roles-list');
 *   };
 *
 *   return (
 *     <Formik enableReinitialize initialValues={values} onSubmit={handleSubmit}>
 *       ...
 *     </Formik>
 *   );
 * };
 */
export function useEditPageSync<T extends Record<string, unknown>>(
  options: UseEditPageSyncOptions<T>,
): UseEditPageSyncReturn<T> {
  const {
    entitySelector,
    fetchAction,
    initialValues,
    postProcess,
    idOverride,
  } = options;

  const router = useRouter();
  const dispatch = useAppDispatch();

  // Get ID from router or override
  const routerId = router.query.id;
  const id =
    idOverride ?? (Array.isArray(routerId) ? routerId[0] : routerId) ?? null;

  // Local state for form values
  const [values, setValues] = useState<T>(initialValues);
  const [isLoading, setIsLoading] = useState(false);
  const [isFound, setIsFound] = useState(false);

  // Get entity from Redux store
  const rawEntity = useAppSelector(entitySelector);

  // Handle both array and single entity - when single entity, it's stored directly
  // When array, it means we fetched a list (shouldn't happen in edit pages)
  // Filter out primitive types (number, boolean, string) from the slice state
  const entity = (() => {
    if (rawEntity === null || rawEntity === undefined) return null;
    if (typeof rawEntity !== 'object') return null; // Filter out primitives
    if (Array.isArray(rawEntity)) {
      return rawEntity.length === 1 ? (rawEntity[0] as T) : null;
    }
    return rawEntity as T;
  })();

  // Fetch entity when ID changes
  useEffect(() => {
    if (id) {
      setIsLoading(true);
      dispatch(fetchAction({ id }))
        .then(() => setIsLoading(false))
        .catch(() => setIsLoading(false));
    }
  }, [id, dispatch, fetchAction]);

  // Sync form values when entity changes
  useEffect(() => {
    if (entity && typeof entity === 'object' && !Array.isArray(entity)) {
      // Build new values by copying from entity to initial structure
      const newValues = { ...initialValues };

      Object.keys(initialValues).forEach((key) => {
        if (key in entity) {
          (newValues as Record<string, unknown>)[key] = entity[key as keyof T];
        }
      });

      // Apply post-processing if provided
      const finalValues = postProcess
        ? postProcess(newValues, initialValues)
        : newValues;

      setValues(finalValues);
      setIsFound(true);
    }
  }, [entity, initialValues, postProcess]);

  return {
    values,
    setValues,
    id,
    isLoading,
    isFound,
  };
}

/**
 * Simplified version that returns just values tuple
 * for drop-in replacement in existing code
 *
 * @example
 * const [initialValues, setInitialValues] = useEditPageSyncSimple({
 *   entitySelector: (state) => state.roles.roles,
 *   fetchAction: fetch,
 *   initialValues: initVals,
 * });
 */
export function useEditPageSyncSimple<T extends Record<string, unknown>>(
  options: UseEditPageSyncOptions<T>,
): [T, React.Dispatch<React.SetStateAction<T>>] {
  const { values, setValues } = useEditPageSync(options);
  return [values, setValues];
}

export default useEditPageSync;
