'use client';

import { useCallback, useEffect, useRef, useState } from 'react';
import { useQuery, useQueryClient } from '@tanstack/react-query';

import { apiFetch } from '@/lib/api/client';
import type { TablePreferences, UserPreferences } from '@/lib/db/schema/users';

interface MeResponse {
  data: {
    preferences?: UserPreferences;
  };
}

/**
 * Reads + writes the current user's per-table column visibility, backed
 * by `user_profiles.preferences.tablePreferences[entityType]`. Returns
 * the hidden-columns set plus a `setHidden(next)` mutator.
 *
 * Writes are debounced (~600ms) so dragging a checkbox list back and
 * forth doesn't spam the API. The local state is updated immediately
 * for instant UX; the network round-trip is best-effort and silently
 * swallowed on failure (preferences are recoverable from local state
 * for the current session).
 *
 * `defaultHidden` applies ONLY when the user has never saved a
 * preference for this entity (the stored entry is `undefined`). Once
 * the user explicitly toggles any column, their saved list takes over
 * - including the case where they've intentionally cleared it to an
 * empty array (which means "show everything", overriding defaults).
 */
export function useTablePreferences(entityType: string, defaultHidden: string[] = []) {
  const queryClient = useQueryClient();

  const meQuery = useQuery<MeResponse>({
    queryKey: ['me'],
    queryFn: ({ signal }) => apiFetch<MeResponse>('/api/v1/me', { signal }),
    staleTime: 5 * 60_000,
  });

  const remoteEntry = meQuery.data?.data.preferences?.tablePreferences?.[entityType];
  const remoteHidden = remoteEntry?.hiddenColumns;
  const remoteDensity = remoteEntry?.density;
  const remoteDimensionUnit = remoteEntry?.dimensionUnit;
  // Local edits win over the server-loaded prefs. The render-phase
  // derivation below (`localHidden ?? remoteHidden ?? defaultHidden`)
  // replaces the prior useEffect(setLocalHidden, [remoteHidden]) sync
  // that the Compiler flagged as set-state-in-effect.
  const [localHidden, setLocalHidden] = useState<string[] | null>(null);
  const [localDensity, setLocalDensity] = useState<'comfortable' | 'compact' | null>(null);
  const [localDimensionUnit, setLocalDimensionUnit] = useState<'ft' | 'm' | null>(null);

  const debounceRef = useRef<ReturnType<typeof setTimeout> | null>(null);

  // Single PATCH for both hidden + density edits. Each setter writes to
  // its local state immediately for instant UX, then the debounced
  // network round-trip merges the entity's current preferences.
  const flush = useCallback(
    (patch: Partial<TablePreferences>) => {
      if (debounceRef.current) clearTimeout(debounceRef.current);
      debounceRef.current = setTimeout(() => {
        const existing = meQuery.data?.data.preferences?.tablePreferences ?? {};
        const merged: TablePreferences = {
          ...(existing[entityType] ?? {}),
          ...patch,
        };
        const updated: Record<string, TablePreferences> = {
          ...existing,
          [entityType]: merged,
        };

        queryClient.setQueryData<MeResponse>(['me'], (old) => {
          if (!old) return old;
          return {
            ...old,
            data: {
              ...old.data,
              preferences: {
                ...(old.data.preferences ?? {}),
                tablePreferences: updated,
              },
            },
          };
        });

        apiFetch('/api/v1/me', {
          method: 'PATCH',
          body: { preferences: { tablePreferences: updated } },
        }).catch(() => {
          // Network failures are non-fatal - the local UI keeps the
          // chosen visibility for the rest of the session.
        });
      }, 600);
    },
    [entityType, meQuery.data, queryClient],
  );

  const setHidden = useCallback(
    (next: string[]) => {
      setLocalHidden(next);
      flush({ hiddenColumns: next });
    },
    [flush],
  );

  const setDensity = useCallback(
    (next: 'comfortable' | 'compact') => {
      setLocalDensity(next);
      flush({ density: next });
    },
    [flush],
  );

  const setDimensionUnit = useCallback(
    (next: 'ft' | 'm') => {
      setLocalDimensionUnit(next);
      flush({ dimensionUnit: next });
    },
    [flush],
  );

  // Cleanup pending timer on unmount so React doesn't warn about
  // setting state after the component is gone.
  useEffect(
    () => () => {
      if (debounceRef.current) clearTimeout(debounceRef.current);
    },
    [],
  );

  // Resolution order: local optimistic → remote saved → caller defaults.
  // The `remoteHidden ?? defaultHidden` step is what gives us the "hide
  // latestStage for fresh users, but respect their override once they
  // touch it" behavior - saved value (even []) wins, defaults only fill
  // the never-saved case.
  const resolved = localHidden ?? remoteHidden ?? defaultHidden;
  const density: 'comfortable' | 'compact' = localDensity ?? remoteDensity ?? 'comfortable';
  // Dimension unit: local optimistic → remote saved → 'ft' fallback (matches
  // the existing canonical-format-in-feet shape of the underlying columns).
  const dimensionUnit: 'ft' | 'm' = localDimensionUnit ?? remoteDimensionUnit ?? 'ft';

  return {
    hidden: resolved,
    setHidden,
    density,
    setDensity,
    dimensionUnit,
    setDimensionUnit,
    isLoaded: !meQuery.isLoading,
  };
}
