import React, { createContext, useCallback, useContext, useEffect, useMemo } from 'react';
import Logger from 'src/application/logging/logger';
import { LocalStorageEventListener } from 'src/application/models/localStorageEventListener';

export enum LocalStorageKeys {
  ChatId = 'v1-spiffy-chat-session-id',
  SpiffyOnOverride = 'spiffy_on',
  EnviveOnOverride = 'envive_on',
}

interface LocalStorageContextType {
  setItem: (key: string, value: string) => void;
  getItem: (key: string) => string | null;
  setSpiffyOnFeatureFlag: (value: boolean | null) => void;
  getSpiffyOnFeatureFlag: () => string | null;
  attachListener: (listener: LocalStorageEventListener) => void;
  detachListener: (listener: LocalStorageEventListener) => void;
  isAvailable: boolean;
}

const LocalStorageContext = createContext<LocalStorageContextType | null>(null);

const logger = new Logger('localStorageContext');

export const LocalStorageProvider: React.FC<{ children: React.ReactNode }> = ({ children }) => {
  const isAvailable = useMemo(() => {
    try {
      return typeof window !== 'undefined' && !!window.localStorage;
    } catch {
      return false;
    }
  }, []);

  useEffect(() => {
    if (!isAvailable) {
      logger.logError('localStorage is not available', undefined);
    }
  }, [isAvailable]);

  const setItem = useCallback(
    (key: string, value: string) => {
      if (!isAvailable) return;
      localStorage.setItem(key, value);
      window.dispatchEvent(new StorageEvent('storage', { key, newValue: value }));
    },
    [isAvailable],
  );

  const getItem = useCallback(
    (key: string) => {
      if (!isAvailable) return null;
      return localStorage.getItem(key);
    },
    [isAvailable],
  );

  const setSpiffyOnFeatureFlag = useCallback(
    (value: boolean | null): void => {
      if (!isAvailable) return;
      if (value === true) {
        localStorage.setItem(LocalStorageKeys.SpiffyOnOverride, 'true');
      } else if (value === false) {
        localStorage.setItem(LocalStorageKeys.SpiffyOnOverride, 'false');
      }
    },
    [isAvailable],
  );

  const getSpiffyOnFeatureFlag = useCallback((): string | null => {
    if (!isAvailable) return null;
    return localStorage.getItem(LocalStorageKeys.SpiffyOnOverride);
  }, [isAvailable]);

  const listenersCache = useMemo(() => new Map<string, (event: StorageEvent) => void>(), []);

  const listenerForKey = useCallback(
    (listener: LocalStorageEventListener): ((event: StorageEvent) => void) => {
      if (!listenersCache.has(listener.storageKey)) {
        const handler = (event: StorageEvent) => {
          if (event.key !== listener.storageKey) {
            return;
          }
          logger.logDebug(`Storage event key=${event.key}, value=`, event.newValue);
          listener.listener(event);
        };
        listenersCache.set(listener.storageKey, handler);
      }
      return listenersCache.get(listener.storageKey)!;
    },
    [listenersCache],
  );

  const attachListener = useCallback(
    (listener: LocalStorageEventListener): void => {
      if (!isAvailable) return;
      window.addEventListener('storage', listenerForKey(listener));
    },
    [isAvailable, listenerForKey],
  );

  const detachListener = useCallback(
    (listener: LocalStorageEventListener): void => {
      if (!isAvailable) return;
      window.removeEventListener('storage', listenerForKey(listener));
    },
    [isAvailable, listenerForKey],
  );

  const value = useMemo(
    () => ({
      setItem,
      getItem,
      setSpiffyOnFeatureFlag,
      getSpiffyOnFeatureFlag,
      attachListener,
      detachListener,
      isAvailable,
    }),
    [
      setItem,
      getItem,
      setSpiffyOnFeatureFlag,
      getSpiffyOnFeatureFlag,
      attachListener,
      detachListener,
      isAvailable,
    ],
  );

  return <LocalStorageContext.Provider value={value}>{children}</LocalStorageContext.Provider>;
};

export const useLocalStorage = () => {
  const context = useContext(LocalStorageContext);
  if (!context) {
    throw new Error('useLocalStorage must be used within a LocalStorageProvider');
  }
  return context;
};
