import { getToken } from "@/config/func";
import { useEffect, useRef, useState, useCallback } from "react";
import type { Socket } from "socket.io-client";
import { io } from "socket.io-client";
import type { NotificationPayload } from "@/types/notification.types";

export type { NotificationPayload };

export interface NotificationSocketOptions {
  serverUrl?: string;
  token: string;
  autoConnect?: boolean;
  onNotification?: (notification: NotificationPayload) => void;
  onJoined?: (data: { room: string; message: string }) => void;
  onError?: (error: { message: string }) => void;
  onConnect?: () => void;
  onDisconnect?: (reason: string) => void;
}

export interface NotificationSocketState {
  isConnected: boolean;
  isConnecting: boolean;
  room: string | null;
  error: string | null;
}

export function useNotificationSocket(options: NotificationSocketOptions) {
  const {
    serverUrl = import.meta.env.VITE_SOCKET_URL,
    token = getToken(),
    autoConnect = true,
    onNotification,
    onJoined,
    onError,
    onConnect,
    onDisconnect,
  } = options;

  const socketRef = useRef<Socket | null>(null);
  const [state, setState] = useState<NotificationSocketState>({
    isConnected: false,
    isConnecting: false,
    room: null,
    error: null,
  });

  // Store callbacks in refs to avoid recreating socket on callback changes
  const callbacksRef = useRef({
    onNotification,
    onJoined,
    onError,
    onConnect,
    onDisconnect,
  });

  useEffect(() => {
    callbacksRef.current = {
      onNotification,
      onJoined,
      onError,
      onConnect,
      onDisconnect,
    };
  }, [onNotification, onJoined, onError, onConnect, onDisconnect]);

  const connect = useCallback(() => {
    if (socketRef.current?.connected) {
      return;
    }

    if (!token) {
      setState((prev) => ({
        ...prev,
        error: "Token is required for connection",
        isConnecting: false,
      }));
      return;
    }

    setState((prev) => ({ ...prev, isConnecting: true, error: null }));

    // Clean token - remove "Bearer " prefix if present
    const cleanToken = token.startsWith("Bearer ") ? token.substring(7) : token;

    const socket = io(`${serverUrl}/notifications`, {
      transports: ["websocket", "polling"],
      reconnection: true,
      reconnectionDelay: 1000,
      reconnectionAttempts: 5,
      auth: {
        token: cleanToken,
      },
    });

    socket.on("connect", () => {
      setState((prev) => ({
        ...prev,
        isConnected: true,
        isConnecting: false,
        error: null,
      }));
      callbacksRef.current.onConnect?.();
    });

    socket.on("disconnect", (reason: string) => {
      setState((prev) => ({
        ...prev,
        isConnected: false,
        isConnecting: false,
        room: null,
      }));
      callbacksRef.current.onDisconnect?.(reason);
    });

    socket.on("connect_error", (error: Error) => {
      setState((prev) => ({
        ...prev,
        isConnected: false,
        isConnecting: false,
        error: error.message || "Connection failed",
      }));
      callbacksRef.current.onError?.({
        message: error.message || "Connection failed",
      });
    });

    socket.on("joined", (data: { room: string; message: string }) => {
      setState((prev) => ({
        ...prev,
        room: data.room,
      }));
      callbacksRef.current.onJoined?.(data);
    });

    socket.on("notifications", (data: NotificationPayload) => {
      callbacksRef.current.onNotification?.(data);
    });

    socket.on("error", (error: { message: string }) => {
      setState((prev) => ({
        ...prev,
        error: error.message,
      }));
      callbacksRef.current.onError?.(error);
    });

    socketRef.current = socket;
  }, [serverUrl, token]);

  const disconnect = useCallback(() => {
    if (socketRef.current) {
      socketRef.current.disconnect();
      socketRef.current = null;
      setState({
        isConnected: false,
        isConnecting: false,
        room: null,
        error: null,
      });
    }
  }, []);

  useEffect(() => {
    if (autoConnect && token) {
      connect();
    }

    return () => {
      disconnect();
    };
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, [autoConnect, token]);

  return {
    ...state,
    connect,
    disconnect,
    socket: socketRef.current,
  };
}
