import { createContext, useCallback, useEffect, useRef, useState } from 'react';
import { ProjectState, SendableProjectState } from '../types';
import localforage from 'localforage';
import { fromSendable, toSendable } from '../sandbox/sandboxTools';
import useStorageBackedState from './useStorageBackedState';
import audioContext from '../audio/utils/audioContext';

const STORAGE_KEY_PREFIX = `saved-project `;

const toStorageKey = (name: string) => `${STORAGE_KEY_PREFIX}${name}`;

export const useProjectManagement = (projectState: ProjectState, onLoadProject: (projectState: ProjectState) => void) => {
  const [currentProjectName, setCurrentProjectName] = useStorageBackedState<string | null>(null, 'open-project-name');
  const [message, setMessage] = useState<String | null>(null);
  const [projectStorageKeyList, setProjectStorageKeyList] = useState<string[]>([]);
  const [lastSavedState, setLastSavedState] = useState<ProjectState | null>(null);
  const isFirstRunRef = useRef(true);
  const isEmpty = projectState.tracks.length === 0;

  const hasChanges = projectState !== lastSavedState;

  const refreshStorageKeyList = useCallback(() => {
    localforage.keys().then((keys) => {
      const projectKeys = keys.filter(k => k.startsWith(STORAGE_KEY_PREFIX));
      if (projectKeys.length) {
        setProjectStorageKeyList(projectKeys);
      }
    });
  }, [setProjectStorageKeyList]);

  useEffect(() => {
    if (isFirstRunRef.current) {
      isFirstRunRef.current = false;
      refreshStorageKeyList();
    }
  }, [refreshStorageKeyList]);

  const saveProject = useCallback(
    () => {
      if (!currentProjectName) {
        setMessage('Please give your project a name first.');
      } else {
        setMessage('Saving...');
        localforage.setItem(toStorageKey(currentProjectName), toSendable(projectState)).then(() => {
          setLastSavedState(projectState);
          setMessage(null);
          refreshStorageKeyList();
        });
      }
    },
    [projectState, setMessage, setLastSavedState, currentProjectName, refreshStorageKeyList]
  );

  const saveProjectAs = useCallback(
    (name: string) => {
      if (projectStorageKeyList.includes(toStorageKey(name))) {
        setMessage('A project already exists with that name!');
      } else {
        setMessage('Saving...');
        setCurrentProjectName(name);
        localforage.setItem(toStorageKey(name), toSendable(projectState)).then(() => {
          setLastSavedState(projectState);
          setMessage(null);
          refreshStorageKeyList();
        });
      }
    },
    [projectState, refreshStorageKeyList, setLastSavedState, projectStorageKeyList, setCurrentProjectName]
  );

  const loadProject = useCallback(
    (name: string) => {
      setMessage('Loading...');
      localforage.getItem(toStorageKey(name)).then((projectState: unknown) => {
        const state = fromSendable(projectState as SendableProjectState);
        setLastSavedState(state);
        onLoadProject(state)
        setCurrentProjectName(name);
        setMessage(null);
      })
    },
    [setMessage, onLoadProject, setCurrentProjectName]
  );

  const startNew = useCallback(() => {
    const newProject = { sampleRate: audioContext.sampleRate, tracks: [] };
    setLastSavedState(newProject);
    onLoadProject(newProject);
  }, [onLoadProject]);

  return {
    hasChanges,
    isEmpty,
    message,
    currentProjectName,
    startNew,
    saveProject,
    saveProjectAs,
    loadProject,
    projectList: projectStorageKeyList.map((k) => k.replace(STORAGE_KEY_PREFIX, ''))
  }
}

export const ProjectManagementContext = createContext<ReturnType<typeof useProjectManagement>>(undefined as never);
