import { createContext, useState, useMemo, useRef, useEffect, useCallback } from 'react';
import { CommandSettings, SettingsConfig, ProjectState } from '../types';
import runCommand from '../audio/commands/runCommand';

export const useCommandSettings = (code: string, projectState: ProjectState) => {
  const [commandSettings, setCommandSettings] = useState<CommandSettings<any>>({});
  const [commandSettingsWithValues, setCommandSettingsWithValues] = useState<{[key: string]: SettingsConfig<any> & { value: any }}>({});
  const settingValues = useMemo<{[key: string]: any}>(
    () => Object.keys(commandSettingsWithValues).reduce((a, k) => ({
      ...a,
      [k]: commandSettingsWithValues[k].value
    }), {}),
    [commandSettingsWithValues]
  );

  const lastStringifiedSettings = useRef('');
  const checkSettingsTimeoutRef = useRef<ReturnType<typeof setTimeout> | null>(null);

  useEffect(
    () => {
      const currentStringifiedSettings = JSON.stringify(commandSettings);
      if (lastStringifiedSettings.current !== currentStringifiedSettings) {
        setCommandSettingsWithValues(
          Object.keys(commandSettings).reduce((a, k) => ({
            ...a,
            [k]: {
              ...commandSettings[k],
              value: commandSettings[k].default
            }
          }),
          {})
        );
        lastStringifiedSettings.current = currentStringifiedSettings;
      }
    },
    [commandSettings, setCommandSettingsWithValues]
  );

  useEffect(
    () => {
      checkSettingsTimeoutRef.current && clearTimeout(checkSettingsTimeoutRef.current);
      checkSettingsTimeoutRef.current = setTimeout(() => {
        // run the command with an empty state & selection to extract command settings
        runCommand(
          code,
          setCommandSettings
        )(
          { sampleRate: projectState.sampleRate, tracks: [] },
          [[0, 0], []],
          {},
          true
        );
      }, 150);
    },
    [code, setCommandSettings, projectState.sampleRate]
  );

  const sortedSettings = useMemo(() => {
    return Object.entries(commandSettingsWithValues).map(([id, config]) => ({
      id,
      ...config
    })).sort((a, b) => Number(a.id) < Number(b.id) ? -1 : 1);
  }, [commandSettingsWithValues]);

  const updateSetting = useCallback((id: string, value: any) => {
    setCommandSettingsWithValues({
      ...commandSettingsWithValues,
      [id]: {
        ...commandSettingsWithValues[id],
        value
      }
    });
  }, [commandSettingsWithValues, setCommandSettingsWithValues]);

  return {
    sortedSettings,
    settingValues,
    updateSetting,
    setCommandSettings
  }
}

export const CommandSettingsContext = createContext<ReturnType<typeof useCommandSettings>>(undefined as never);
