import {
  getLocalStorageItemAsJSON,
  LocalStorageKey,
  setLocalStorageItemAsJSON
} from '@/helpers/storage';
import { Resolution, type TDeviceSettings } from '@/types';
import {
  createContext,
  memo,
  useCallback,
  useEffect,
  useMemo,
  useState
} from 'react';
import { useAvailableDevices } from './hooks/use-available-devices';

const DEFAULT_DEVICE_SETTINGS: TDeviceSettings = {
  microphoneId: undefined,
  playbackId: undefined,
  webcamId: undefined,
  webcamResolution: Resolution['720p'],
  webcamFramerate: 30,
  echoCancellation: false,
  noiseSuppression: false,
  noiseSuppressionEnhanced: false,
  noiseSuppressionRnnoise: false,
  noiseSuppressionDeepFilterNet: true,
  keyboardSuppression: false,
  voiceSensitivity: 0,
  autoGainControl: false,
  shareSystemAudio: false,
  screenResolution: Resolution['720p'],
  screenFramerate: 30,
  screenAudioBitrate: 128
};

const sanitizeDeviceSettings = (
  settings: TDeviceSettings | null | undefined
): TDeviceSettings => {
  const merged = {
    ...DEFAULT_DEVICE_SETTINGS,
    ...(settings ?? {})
  };

  if (merged.noiseSuppressionRnnoise && merged.noiseSuppressionEnhanced) {
    merged.noiseSuppressionEnhanced = false;
  }

  if (merged.noiseSuppressionDeepFilterNet) {
    merged.noiseSuppressionEnhanced = false;
    merged.noiseSuppressionRnnoise = false;
  }

  const hasWasmSuppression =
    merged.noiseSuppressionDeepFilterNet || merged.noiseSuppressionRnnoise;
  if (hasWasmSuppression) {
    merged.echoCancellation = false;
    merged.noiseSuppression = false;
    merged.autoGainControl = false;
  }

  if (!Number.isFinite(merged.voiceSensitivity)) {
    merged.voiceSensitivity = DEFAULT_DEVICE_SETTINGS.voiceSensitivity;
  }

  merged.voiceSensitivity = Math.min(100, Math.max(0, merged.voiceSensitivity));
  return merged;
};

export type TDevicesProvider = {
  loading: boolean;
  devices: TDeviceSettings;
  saveDevices: (newDevices: TDeviceSettings) => void;
};

const DevicesProviderContext = createContext<TDevicesProvider>({
  loading: false,
  devices: DEFAULT_DEVICE_SETTINGS,
  saveDevices: () => {}
});

type TDevicesProviderProps = {
  children: React.ReactNode;
};

const DevicesProvider = memo(({ children }: TDevicesProviderProps) => {
  const [loading, setLoading] = useState<boolean>(true);
  const [devices, setDevices] = useState<TDeviceSettings>(
    sanitizeDeviceSettings(DEFAULT_DEVICE_SETTINGS)
  );
  const { loading: devicesLoading } = useAvailableDevices();

  const saveDevices = useCallback((newDevices: TDeviceSettings) => {
    const sanitized = sanitizeDeviceSettings(newDevices);
    setDevices(sanitized);
    setLocalStorageItemAsJSON<TDeviceSettings>(
      LocalStorageKey.DEVICES_SETTINGS,
      sanitized
    );
  }, []);

  useEffect(() => {
    if (devicesLoading) return;

    const savedSettings = getLocalStorageItemAsJSON<TDeviceSettings>(
      LocalStorageKey.DEVICES_SETTINGS
    );

    if (savedSettings) {
      setDevices(sanitizeDeviceSettings(savedSettings));
    }

    setLoading(false);
  }, [devicesLoading]);

  const contextValue = useMemo<TDevicesProvider>(
    () => ({
      loading,
      devices,
      saveDevices
    }),
    [loading, devices, saveDevices]
  );

  return (
    <DevicesProviderContext.Provider value={contextValue}>
      {children}
    </DevicesProviderContext.Provider>
  );
});

export { DevicesProvider, DevicesProviderContext };
