/**
 * React Hook for Socket.io Real-Time Updates
 *
 * Provides easy-to-use hooks for real-time data in React components.
 */

import { useState, useEffect, useCallback, useRef } from 'react';
import { getSocketClient, RealtimeSocketClient } from './socketClient';
import type {
  ConnectionStatus,
  TransparencyEvent,
  RoomType,
  TransparencyEventType,
  ConnectionMetrics,
  LiveFeedItem,
} from './types';
import { toFeedItem } from './events';

/**
 * Hook configuration options
 */
export interface UseSocketOptions {
  autoConnect?: boolean;
  rooms?: RoomType[];
  eventTypes?: TransparencyEventType[];
  userId?: string;
  maxEvents?: number;
}

/**
 * Hook return type
 */
export interface UseSocketReturn {
  status: ConnectionStatus;
  isConnected: boolean;
  events: TransparencyEvent[];
  latestEvent: TransparencyEvent | null;
  metrics: ConnectionMetrics;
  connect: () => void;
  disconnect: () => void;
  joinRoom: (room: RoomType) => Promise<boolean>;
  leaveRoom: (room: RoomType) => Promise<boolean>;
  clearEvents: () => void;
}

/**
 * Main socket hook for real-time updates
 */
export function useSocket(options: UseSocketOptions = {}): UseSocketReturn {
  const {
    autoConnect = true,
    rooms = [],
    eventTypes = [],
    userId,
    maxEvents = 100,
  } = options;

  const [status, setStatus] = useState<ConnectionStatus>('disconnected');
  const [events, setEvents] = useState<TransparencyEvent[]>([]);
  const [latestEvent, setLatestEvent] = useState<TransparencyEvent | null>(null);
  const [metrics, setMetrics] = useState<ConnectionMetrics>({
    status: 'disconnected',
    eventsReceived: 0,
    reconnectAttempts: 0,
    rooms: [],
  });

  const clientRef = useRef<RealtimeSocketClient | null>(null);
  const cleanupRef = useRef<(() => void)[]>([]);

  // Initialize client
  useEffect(() => {
    if (typeof window === 'undefined') return;

    const client = getSocketClient({ auth: { userId } });
    clientRef.current = client;

    // Set up listeners
    const unsubStatus = client.onStatusChange((newStatus) => {
      setStatus(newStatus);
      setMetrics(client.getMetrics());
    });

    const unsubEvent = client.onEvent((event) => {
      setLatestEvent(event);
      setEvents((prev) => {
        const updated = [event, ...prev];
        return updated.slice(0, maxEvents);
      });
      setMetrics(client.getMetrics());
    });

    cleanupRef.current = [unsubStatus, unsubEvent];

    // Auto connect
    if (autoConnect) {
      client.connect();
    }

    // Initial metrics
    setMetrics(client.getMetrics());

    return () => {
      cleanupRef.current.forEach((cleanup) => cleanup());
    };
  }, [autoConnect, userId, maxEvents]);

  // Join initial rooms
  useEffect(() => {
    if (!clientRef.current || status !== 'connected') return;

    rooms.forEach((room) => {
      clientRef.current?.joinRoom(room);
    });
  }, [status, rooms]);

  // Subscribe to event types
  useEffect(() => {
    if (!clientRef.current || status !== 'connected' || eventTypes.length === 0) return;

    clientRef.current.subscribeToTypes(eventTypes);
  }, [status, eventTypes]);

  const connect = useCallback(() => {
    clientRef.current?.connect();
  }, []);

  const disconnect = useCallback(() => {
    clientRef.current?.disconnect();
  }, []);

  const joinRoom = useCallback(async (room: RoomType) => {
    return clientRef.current?.joinRoom(room) ?? false;
  }, []);

  const leaveRoom = useCallback(async (room: RoomType) => {
    return clientRef.current?.leaveRoom(room) ?? false;
  }, []);

  const clearEvents = useCallback(() => {
    setEvents([]);
    setLatestEvent(null);
  }, []);

  return {
    status,
    isConnected: status === 'connected',
    events,
    latestEvent,
    metrics,
    connect,
    disconnect,
    joinRoom,
    leaveRoom,
    clearEvents,
  };
}

/**
 * Hook for live feed display
 */
export function useLiveFeed(options: UseSocketOptions = {}): {
  items: LiveFeedItem[];
  isConnected: boolean;
  status: ConnectionStatus;
  clearFeed: () => void;
} {
  const { events, isConnected, status, clearEvents } = useSocket(options);

  const items = events.map((event) => toFeedItem(event));

  return {
    items,
    isConnected,
    status,
    clearFeed: clearEvents,
  };
}

/**
 * Hook for tracking a specific plant's real-time updates
 */
export function usePlantUpdates(plantId: string): {
  events: TransparencyEvent[];
  isConnected: boolean;
} {
  return useSocket({
    rooms: [`plant:${plantId}` as RoomType],
    eventTypes: [
      'plant.registered',
      'plant.cloned',
      'plant.transferred',
      'plant.updated',
      'transport.started',
      'transport.completed',
    ],
  }) as { events: TransparencyEvent[]; isConnected: boolean };
}

/**
 * Hook for tracking a specific farm's real-time updates
 */
export function useFarmUpdates(farmId: string): {
  events: TransparencyEvent[];
  isConnected: boolean;
} {
  return useSocket({
    rooms: [`farm:${farmId}` as RoomType],
    eventTypes: [
      'farm.registered',
      'farm.updated',
      'batch.started',
      'batch.harvested',
      'agent.alert',
    ],
  }) as { events: TransparencyEvent[]; isConnected: boolean };
}

/**
 * Hook for connection status only (lightweight)
 */
export function useConnectionStatus(): {
  status: ConnectionStatus;
  isConnected: boolean;
  latency: number | undefined;
} {
  const { status, isConnected, metrics } = useSocket({ autoConnect: true });

  return {
    status,
    isConnected,
    latency: metrics.latency,
  };
}

/**
 * Hook for event counts (useful for notification badges)
 */
export function useEventCount(options: UseSocketOptions = {}): {
  count: number;
  unreadCount: number;
  markAllRead: () => void;
} {
  const { events } = useSocket(options);
  const [readCount, setReadCount] = useState(0);

  const markAllRead = useCallback(() => {
    setReadCount(events.length);
  }, [events.length]);

  return {
    count: events.length,
    unreadCount: Math.max(0, events.length - readCount),
    markAllRead,
  };
}

export default useSocket;
