import { io, Socket } from 'socket.io-client'

const SOCKET_URL = (() => {
  const url = process.env.NEXT_PUBLIC_API_URL || 'http://localhost:8008'
  try {
    return new URL(url).origin
  } catch {
    return url
  }
})()

type Listener = (data: any) => void

const HEARTBEAT_INTERVAL_MS = 30_000

class SocketService {
  private socket: Socket | null = null
  private token: string | null = null
  private listeners = new Map<string, Set<Listener>>()
  private heartbeatTimer: ReturnType<typeof setInterval> | null = null

  // ── Conexão ────────────────────────────────────────────────────────────────

  connect(token?: string) {
    if (token) this.token = token
    if (this.socket?.connected) return

    this.socket = io(SOCKET_URL, {
      auth: { token: this.token },
      transports: ['websocket', 'polling'],
      reconnectionAttempts: 10,
      reconnectionDelay: 2000,
    })

    this.socket.on('connect', () => {
      this._emit('connected', { id: this.socket?.id })
      this._startHeartbeat()
    })

    this.socket.on('disconnect', (reason) => {
      this._stopHeartbeat()
      this._emit('disconnected', { reason })
    })

    this.socket.on('connect_error', (err) => {
      console.warn('[Socket] connect_error:', err.message)
    })

    this.socket.onAny((event, data) => {
      console.log('[Socket Event Received]', event, data)
      this._emit(event, data)
      if (event !== '*') this._emit('*', { event, data })
    })
  }

  disconnect() {
    this._stopHeartbeat()
    this.socket?.disconnect()
    this.socket = null
    this.token = null
  }

  // ── Emit para o servidor ───────────────────────────────────────────────────

  emit(event: string, ...args: any[]) {
    this.socket?.emit(event, ...args)
  }

  joinInstance(instanceId: string) {
    if (this.socket?.connected) {
      this.socket.emit('join:instance', instanceId)
    } else {
      // Aguarda conexão e então entra na sala
      const onConnect = () => {
        this.socket?.emit('join:instance', instanceId)
        this.off('connected', onConnect)
      }
      this.on('connected', onConnect)
    }
  }

  joinChat(chatId: string) {
    if (this.socket?.connected) {
      this.socket.emit('join:chat', chatId)
    } else {
      const onConnect = () => {
        this.socket?.emit('join:chat', chatId)
        this.off('connected', onConnect)
      }
      this.on('connected', onConnect)
    }
  }

  leaveChat(chatId: string) {
    this.socket?.emit('leave:chat', chatId)
  }

  // ── Listeners ─────────────────────────────────────────────────────────────

  on(event: string, cb: Listener) {
    if (!this.listeners.has(event)) this.listeners.set(event, new Set())
    this.listeners.get(event)!.add(cb)
  }

  off(event: string, cb: Listener) {
    this.listeners.get(event)?.delete(cb)
  }

  // ── Estado ────────────────────────────────────────────────────────────────

  get connected() {
    return this.socket?.connected ?? false
  }

  getSocket() {
    return this.socket
  }

  // ── Privado ───────────────────────────────────────────────────────────────

  private _startHeartbeat() {
    this._stopHeartbeat()
    this.heartbeatTimer = setInterval(() => {
      if (this.socket?.connected) this.socket.emit('user:heartbeat')
    }, HEARTBEAT_INTERVAL_MS)
  }

  private _stopHeartbeat() {
    if (this.heartbeatTimer) {
      clearInterval(this.heartbeatTimer)
      this.heartbeatTimer = null
    }
  }

  private _emit(event: string, data: any) {
    this.listeners.get(event)?.forEach((cb) => cb(data))
  }
}

export const socketService = new SocketService()
