'use client';

import { useCallback, useState, useTransition, type ReactNode } from 'react';
import { toast } from 'sonner';
import { useTranslations } from '@/components/locale-provider';
import { SaveBar } from './save-bar';

export type SettingsSaveResult = { ok: true } | { ok: false; error: string };

export interface SettingsFormRenderProps<T> {
  value: T;
  setValue: (updater: T | ((previous: T) => T)) => void;
  isDirty: boolean;
  isSaving: boolean;
  error: string | null;
}

interface SettingsFormProps<T> {
  initialValue: T;
  onSave: (value: T) => Promise<SettingsSaveResult>;
  children: (props: SettingsFormRenderProps<T>) => ReactNode;
  isEqual?: (a: T, b: T) => boolean;
  /** When true, draft editing UI still works locally but the sticky save bar is hidden. */
  readOnly?: boolean;
}

function defaultIsEqual<T>(a: T, b: T): boolean {
  return JSON.stringify(a) === JSON.stringify(b);
}

/**
 * Generic settings framework used by every module settings page: tracks a
 * local draft against the last-saved baseline, shows a sticky save bar while
 * dirty, and surfaces inline + toast errors on save failure.
 */
export function SettingsForm<T>({
  initialValue,
  onSave,
  children,
  isEqual = defaultIsEqual,
  readOnly = false
}: SettingsFormProps<T>) {
  const t = useTranslations();
  const [baseline, setBaseline] = useState(initialValue);
  const [value, setValue] = useState(initialValue);
  const [error, setError] = useState<string | null>(null);
  const [isPending, startTransition] = useTransition();

  const isDirty = !readOnly && !isEqual(baseline, value);

  const handleDiscard = useCallback(() => {
    setValue(baseline);
    setError(null);
  }, [baseline]);

  const handleSave = useCallback(() => {
    if (readOnly) {
      return;
    }
    setError(null);
    startTransition(async () => {
      try {
        const result = await onSave(value);
        if (result.ok) {
          setBaseline(value);
          toast.success(t('common.saveSuccess'));
        } else {
          setError(result.error);
          toast.error(result.error || t('common.saveError'));
        }
      } catch {
        setError(t('common.saveError'));
        toast.error(t('common.saveError'));
      }
    });
  }, [onSave, readOnly, t, value]);

  return (
    <div className={readOnly ? 'space-y-6' : 'space-y-6 pb-20'}>
      {children({ value, setValue, isDirty, isSaving: isPending, error })}
      <SaveBar visible={isDirty} saving={isPending} onSave={handleSave} onDiscard={handleDiscard} />
    </div>
  );
}
