/**
 * useReadingSettings — fetch and manage user reading preferences.
 * Preview applies instantly to the reader; API saves are debounced for sliders.
 */

import { useCallback, useEffect, useRef, useState } from "react";
import {
  getReadingSettings,
  updateReadingSettings,
} from "../api/reader";
import type { ReadingSettings } from "../types/reader";

const THEME_COLORS: Record<
  ReadingSettings["theme"],
  Pick<ReadingSettings, "background_color" | "text_color">
> = {
  sepia: { background_color: "#f5f0eb", text_color: "#1a1a1a" },
  dark: { background_color: "#1a1a2e", text_color: "#e0e0e0" },
  light: { background_color: "#ffffff", text_color: "#1a1a1a" },
  paper: { background_color: "#e8e0d4", text_color: "#2c2c2c" },
};

const DEFAULT_SETTINGS: ReadingSettings = {
  font_family: "serif",
  font_size: 18,
  line_height: 1.6,
  margin_width: 16,
  background_color: "#f5f0eb",
  text_color: "#1a1a1a",
  brightness: 100,
  orientation_lock: "auto",
  theme: "sepia",
  created_at: "",
  updated_at: "",
};

const SAVE_DEBOUNCE_MS = 600;

export function applyReadingCssVariables(settings: ReadingSettings): void {
  const root = document.documentElement;
  root.style.setProperty("--reader-bg", settings.background_color);
  root.style.setProperty("--reader-text", settings.text_color);
  root.style.setProperty("--reader-margin", `${settings.margin_width}px`);
  root.style.setProperty("--reader-brightness", `${settings.brightness}%`);
}

function mergeSettings(
  prev: ReadingSettings,
  partial: Partial<ReadingSettings>,
): ReadingSettings {
  const next = { ...prev, ...partial };
  if (partial.theme && THEME_COLORS[partial.theme]) {
    Object.assign(next, THEME_COLORS[partial.theme]);
  }
  return next;
}

export type SettingsPersistMode = "debounced" | "immediate";

export interface UseReadingSettingsReturn {
  settings: ReadingSettings;
  isLoading: boolean;
  error: string | null;
  updateSettings: (
    partial: Partial<ReadingSettings>,
    persist?: SettingsPersistMode,
  ) => void;
  flushSettings: () => Promise<void>;
}

export function useReadingSettings(): UseReadingSettingsReturn {
  const [settings, setSettings] = useState<ReadingSettings>(DEFAULT_SETTINGS);
  const [isLoading, setIsLoading] = useState(true);
  const [error, setError] = useState<string | null>(null);

  const pendingPatch = useRef<Partial<ReadingSettings>>({});
  const saveTimer = useRef<ReturnType<typeof setTimeout> | null>(null);
  const saving = useRef(false);

  const runPersist = useCallback(async () => {
    const payload = { ...pendingPatch.current };
    pendingPatch.current = {};
    if (Object.keys(payload).length === 0 || saving.current) return;

    saving.current = true;
    try {
      const updated = await updateReadingSettings(payload);
      setSettings(updated);
      applyReadingCssVariables(updated);
      setError(null);
    } catch (err: unknown) {
      setError(
        err instanceof Error ? err.message : "Failed to update reading settings",
      );
    } finally {
      saving.current = false;
    }
  }, []);

  const schedulePersist = useCallback(
    (partial: Partial<ReadingSettings>, immediate: boolean) => {
      Object.assign(pendingPatch.current, partial);
      if (saveTimer.current) clearTimeout(saveTimer.current);
      if (immediate) {
        void runPersist();
        return;
      }
      saveTimer.current = setTimeout(() => {
        void runPersist();
      }, SAVE_DEBOUNCE_MS);
    },
    [runPersist],
  );

  useEffect(() => {
    let cancelled = false;
    setIsLoading(true);
    getReadingSettings()
      .then((data) => {
        if (!cancelled) {
          setSettings(data);
          applyReadingCssVariables(data);
        }
      })
      .catch((err: unknown) => {
        if (!cancelled) {
          setError(
            err instanceof Error ? err.message : "Failed to load reading settings",
          );
          applyReadingCssVariables(DEFAULT_SETTINGS);
        }
      })
      .finally(() => {
        if (!cancelled) setIsLoading(false);
      });
    return () => {
      cancelled = true;
      if (saveTimer.current) clearTimeout(saveTimer.current);
    };
  }, []);

  const updateSettings = useCallback(
    (partial: Partial<ReadingSettings>, persist: SettingsPersistMode = "debounced") => {
      setSettings((prev) => {
        const next = mergeSettings(prev, partial);
        applyReadingCssVariables(next);
        return next;
      });
      schedulePersist(partial, persist === "immediate");
    },
    [schedulePersist],
  );

  const flushSettings = useCallback(async () => {
    if (saveTimer.current) clearTimeout(saveTimer.current);
    await runPersist();
  }, [runPersist]);

  return { settings, isLoading, error, updateSettings, flushSettings };
}
