import { useEffect, useState } from 'react';

// Module-singleton animated-background settings — mirrors the useTheme pattern.
// Consumers: ThemeFx (reads), ThemePicker (reads + writes). localStorage only,
// no server/schema change. Ported from /opt/boolab's boocode FX controls.

const ON_KEY = 'boocode.anim-bg';
const DENSITY_KEY = 'boocode.anim-density';
const SPEED_KEY = 'boocode.anim-speed';
const OPACITY_KEY = 'boocode.anim-opacity';

// User-facing slider defaults + ranges. `speed` is a MULTIPLIER applied to each
// canvas's native base speed (so 1 = the tuned default); `density` applies to
// the matrix rain only; `opacity` sets the canvas opacity for both fields.
export const ANIM_DEFAULTS = { density: 0.35, speed: 1, opacity: 0.6 } as const;
export const ANIM_RANGES = {
  density: { min: 0.1, max: 0.6, step: 0.01 },
  speed: { min: 0.3, max: 2, step: 0.05 },
  opacity: { min: 0.1, max: 1, step: 0.01 },
} as const;

type NumParam = 'density' | 'speed' | 'opacity';
const NUM_KEYS: Record<NumParam, string> = {
  density: DENSITY_KEY,
  speed: SPEED_KEY,
  opacity: OPACITY_KEY,
};

function clamp(n: number, lo: number, hi: number): number {
  return Math.min(hi, Math.max(lo, n));
}

function readOn(): boolean {
  try {
    return localStorage.getItem(ON_KEY) !== 'off';
  } catch {
    return true;
  }
}

function readNum(param: NumParam): number {
  const { min, max } = ANIM_RANGES[param];
  const def = ANIM_DEFAULTS[param];
  try {
    const raw = localStorage.getItem(NUM_KEYS[param]);
    if (raw === null) return def;
    const n = Number(raw);
    return Number.isFinite(n) ? clamp(n, min, max) : def;
  } catch {
    return def;
  }
}

interface AnimState {
  on: boolean;
  density: number;
  speed: number;
  opacity: number;
}

let _state: AnimState = {
  on: readOn(),
  density: readNum('density'),
  speed: readNum('speed'),
  opacity: readNum('opacity'),
};

const _subs = new Set<(s: AnimState) => void>();

function notify(): void {
  for (const s of _subs) {
    try {
      s(_state);
    } catch {
      // swallow — bad subscriber shouldn't break others
    }
  }
}

export function getAnimBg(): boolean {
  return _state.on;
}

export function setAnimBg(on: boolean): void {
  _state = { ..._state, on };
  try {
    localStorage.setItem(ON_KEY, on ? 'on' : 'off');
  } catch {
    // quota / disabled
  }
  notify();
}

function setNum(param: NumParam, value: number): void {
  const { min, max } = ANIM_RANGES[param];
  const v = clamp(value, min, max);
  _state = { ..._state, [param]: v };
  try {
    localStorage.setItem(NUM_KEYS[param], String(v));
  } catch {
    // quota / disabled
  }
  notify();
}

export function setAnimDensity(v: number): void {
  setNum('density', v);
}
export function setAnimSpeed(v: number): void {
  setNum('speed', v);
}
export function setAnimOpacity(v: number): void {
  setNum('opacity', v);
}

function useAnimState(): AnimState {
  const [state, setState] = useState(_state);
  useEffect(() => {
    _subs.add(setState);
    setState(_state); // reconcile in case it changed before mount
    return () => {
      _subs.delete(setState);
    };
  }, []);
  return state;
}

export function useAnimBg(): boolean {
  return useAnimState().on;
}

export function useAnimParams(): { density: number; speed: number; opacity: number } {
  const { density, speed, opacity } = useAnimState();
  return { density, speed, opacity };
}
