"use client";

import React, {
  createContext,
  ReactNode,
  useCallback,
  useContext,
  useReducer,
} from "react";

import { ToastContainer } from "@/components/widgets";

export type ToastVariant = "error" | "success" | "warning" | "info";

export interface Toast {
  id: string;
  variant: ToastVariant;
  message: string | React.ReactNode;
  duration?: number;
  classNameContainer?: string;
  classNameToast?: string;
  priority?: number;
  isVisible?: boolean;
  timerId?: number;
  startTime?: number;
}

interface ToastState {
  toasts: Toast[];
  maxVisible: number;
}

type ToastAction =
  | { type: "ADD_TOAST"; payload: Toast }
  | { type: "REMOVE_TOAST"; payload: { id: string } }
  | { type: "CLEAR_TOASTS" }
  | { type: "SET_MAX_VISIBLE"; payload: { max: number } }
  | {
      type: "SET_TOAST_VISIBLE";
      payload: { id: string; isVisible: boolean; startTime?: number };
    }
  | { type: "UPDATE_TOAST_TIMER"; payload: { id: string; timerId: number } };

const initialState: ToastState = {
  toasts: [],
  maxVisible: 3,
};

function toastReducer(state: ToastState, action: ToastAction): ToastState {
  switch (action.type) {
    case "ADD_TOAST":
      const newToasts = [...state.toasts, action.payload].sort(
        (a, b) => (b.priority ?? 0) - (a.priority ?? 0)
      );
      return { ...state, toasts: newToasts };

    case "REMOVE_TOAST":
      return {
        ...state,
        toasts: state.toasts.filter(toast => toast.id !== action.payload.id),
      };

    case "CLEAR_TOASTS":
      return { ...state, toasts: [] };

    case "SET_MAX_VISIBLE":
      return { ...state, maxVisible: action.payload.max };

    case "SET_TOAST_VISIBLE":
      return {
        ...state,
        toasts: state.toasts.map(toast =>
          toast.id === action.payload.id
            ? {
                ...toast,
                isVisible: action.payload.isVisible,
                startTime: action.payload.startTime,
              }
            : toast
        ),
      };

    case "UPDATE_TOAST_TIMER":
      return {
        ...state,
        toasts: state.toasts.map(toast =>
          toast.id === action.payload.id
            ? { ...toast, timerId: action.payload.timerId }
            : toast
        ),
      };

    default:
      return state;
  }
}

interface ToastContextType {
  state: ToastState;
  addToast: (toast: Omit<Toast, "id" | "isVisible">) => string;
  removeToast: (id: string) => void;
  clearToasts: () => void;
  setMaxVisible: (max: number) => void;
  setToastVisible: (id: string, isVisible: boolean, startTime?: number) => void;
  updateToastTimer: (id: string, timerId: number) => void;
}

const ToastContext = createContext<ToastContextType | undefined>(undefined);

interface ToastProviderProps {
  children: ReactNode;
  maxVisible?: number;
}

export const ToastProvider = ({
  children,
  maxVisible = 3,
}: ToastProviderProps) => {
  const [state, dispatch] = useReducer(toastReducer, {
    ...initialState,
    maxVisible,
  });

  const addToast = useCallback((toast: Omit<Toast, "id" | "isVisible">) => {
    const id = Math.random().toString(36).substr(2, 9);
    const newToast = {
      ...toast,
      id,
      priority: toast.priority ?? 0,
      isVisible: false,
    };

    dispatch({ type: "ADD_TOAST", payload: newToast });
    return id;
  }, []);

  const removeToast = useCallback(
    (id: string) => {
      const toast = state.toasts.find(t => t.id === id);
      if (toast?.timerId) {
        clearTimeout(toast.timerId);
      }

      dispatch({ type: "REMOVE_TOAST", payload: { id } });
    },
    [state.toasts]
  );

  const clearToasts = useCallback(() => {
    state.toasts.forEach(toast => {
      if (toast.timerId) {
        clearTimeout(toast.timerId);
      }
    });

    dispatch({ type: "CLEAR_TOASTS" });
  }, [state.toasts]);

  const setMaxVisible = useCallback((max: number) => {
    dispatch({ type: "SET_MAX_VISIBLE", payload: { max } });
  }, []);

  const setToastVisible = useCallback(
    (id: string, isVisible: boolean, startTime?: number) => {
      dispatch({
        type: "SET_TOAST_VISIBLE",
        payload: { id, isVisible, startTime },
      });
    },
    []
  );

  const updateToastTimer = useCallback((id: string, timerId: number) => {
    dispatch({ type: "UPDATE_TOAST_TIMER", payload: { id, timerId } });
  }, []);

  const contextValue = React.useMemo(
    () => ({
      state,
      addToast,
      removeToast,
      clearToasts,
      setMaxVisible,
      setToastVisible,
      updateToastTimer,
    }),
    [
      state,
      addToast,
      removeToast,
      clearToasts,
      setMaxVisible,
      setToastVisible,
      updateToastTimer,
    ]
  );

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

export const useToast = () => {
  const context = useContext(ToastContext);
  if (!context) {
    throw new Error("useToast must be used within ToastProvider");
  }
  return context;
};
