'use client';

import React, {
  createContext,
  useContext,
  ReactNode,
  useMemo,
  useRef,
  useEffect
} from 'react';
import { useLogger, LogEntry, LogLevel } from './use-logger';

const LOG_LEVELS: LogLevel[] = ['info', 'warn', 'error'];

interface LoggerContextType {
  logs: LogEntry[];
  isVisible: boolean;
  toggleVisibility: () => void;
  clearLogs: () => void;
  info: (message: string, payload?: any) => string;
  warn: (message: string, payload?: any) => string;
  error: (message: string, payload?: any) => string;
  isDevelopment: boolean;
  hasError: boolean;
  hasWarning: boolean;
}

const LoggerContext = createContext<LoggerContextType | undefined>(undefined);

let globalAddLogFunction:
  | ((level: string, message: string, payload?: any) => string)
  | null = null;

// temporary queue for logs generated before the logger is initialized
const pendingLogs: Array<{ level: string; message: string; payload?: any }> =
  [];

const createLogFunction =
  (level: LogLevel) => (message: string, payload?: any) => {
    if (
      typeof window !== 'undefined' &&
      process.env.NODE_ENV === 'development'
    ) {
      try {
        if (globalAddLogFunction) {
          globalAddLogFunction(level, message, payload);
        } else {
          pendingLogs.push({ level, message, payload });
        }
      } catch (err) {
        // prevent errors
      }
    }

    return '';
  };

const stableLogger = LOG_LEVELS.reduce((logger, level) => {
  logger[level] = createLogFunction(level);

  return logger;
}, {} as Record<LogLevel, (message: string, payload?: any) => string>);

export const LoggerProvider = ({ children }: { children: ReactNode }) => {
  const loggerHook = useLogger();

  const addLogRef = useRef<
    (level: string, message: string, payload?: any) => string
  >((level, message, payload) => {
    if (LOG_LEVELS.includes(level as LogLevel)) {
      return loggerHook[level as LogLevel](message, payload);
    }
    return '';
  });

  useEffect(() => {
    globalAddLogFunction = addLogRef.current;

    if (pendingLogs.length > 0) {
      pendingLogs.forEach((log) => {
        if (globalAddLogFunction) {
          globalAddLogFunction(log.level, log.message, log.payload);
        }
      });

      pendingLogs.length = 0;
    }

    return () => {
      globalAddLogFunction = null;
    };
  }, []);

  const contextValue = useMemo(
    () => loggerHook,
    [loggerHook.logs, loggerHook.isVisible, loggerHook.isDevelopment] // eslint-disable-line react-hooks/exhaustive-deps
  );

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

export const useLoggerContext = () => {
  const context = useContext(LoggerContext);
  if (context === undefined) {
    throw new Error('useLoggerContext must be used within a LoggerProvider');
  }
  return context;
};

export const devLogger = stableLogger;
