import { ROUTES_ENUM } from '@/routes/route.constant'
import { store } from '@/store/store'
import * as signalR from '@microsoft/signalr'
import { toast } from '@/components/ui'
import Notification from '@/components/ui/Notification'
import { VideoroomAttendanceDto, VideoroomChatDto } from '@/proxy/videoroom/models'

export class SignalRService {
  private connection!: signalR.HubConnection
  private isConnected: boolean = false
  private currentSessionId?: string
  private isKicked: boolean = false

  private onAttendanceUpdate?: (record: VideoroomAttendanceDto) => void
  private onParticipantJoined?: (
    userId: string,
    name: string,
    isTeacher: boolean,
    isActive: boolean,
  ) => void
  private onParticipantLeft?: (payload: {
    userId: string
    sessionId: string
    userName: string
  }) => void
  private onChatMessage?: (message: VideoroomChatDto) => void
  private onParticipantMuted?: (userId: string, isMuted: boolean) => void
  private onHandRaiseReceived?: (studentId: string) => void
  private onHandRaiseDismissed?: (studentId: string) => void
  private onOfferReceived?: (fromUserId: string, offer: RTCSessionDescriptionInit) => void
  private onAnswerReceived?: (fromUserId: string, answer: RTCSessionDescriptionInit) => void
  private onIceCandidateReceived?: (fromUserId: string, candidate: RTCIceCandidateInit) => void
  private onForceCleanup?: () => void

  constructor() {
    const { auth } = store.getState()

    this.connection = new signalR.HubConnectionBuilder()
      .withUrl(`${import.meta.env.VITE_API_URL}/videoroomhub`, {
        accessTokenFactory: () => auth.session.token || '',
      })
      .configureLogging(signalR.LogLevel.Information)
      .build()

    this.setupEventHandlers()
  }

  private setupEventHandlers() {
    if (!this.connection) return

    this.connection.on('AttendanceUpdated', (record: VideoroomAttendanceDto) => {
      this.onAttendanceUpdate?.(record)
    })

    this.connection.on(
      'ParticipantJoined',
      (userId: string, name: string, isTeacher: boolean, isActive: boolean) => {
        this.onParticipantJoined?.(userId, name, isTeacher, isActive)
      },
    )

    this.connection.on(
      'ParticipantLeft',
      (payload: { userId: string; sessionId: string; userName: string }) => {
        this.onParticipantLeft?.(payload)
      },
    )

    this.connection.on('ChatMessage', (message: any) => {
      this.onChatMessage?.(message)
    })

    this.connection.on('ParticipantMuted', (userId: string, isMuted: boolean) => {
      this.onParticipantMuted?.(userId, isMuted)
    })

    this.connection.on('HandRaiseReceived', (payload: any) => {
      this.onHandRaiseReceived?.(payload.studentId)
    })

    this.connection.on('HandRaiseDismissed', (payload: any) => {
      this.onHandRaiseDismissed?.(payload.studentId)
    })

    this.connection.on('ReceiveOffer', (fromUserId: string, offer: RTCSessionDescriptionInit) => {
      this.onOfferReceived?.(fromUserId, offer)
    })

    this.connection.on('ReceiveAnswer', (fromUserId: string, answer: RTCSessionDescriptionInit) => {
      this.onAnswerReceived?.(fromUserId, answer)
    })

    this.connection.on(
      'ReceiveIceCandidate',
      (fromUserId: string, candidate: RTCIceCandidateInit) => {
        this.onIceCandidateReceived?.(fromUserId, candidate)
      },
    )

    this.connection.onreconnected(async () => {
      this.isConnected = true
      toast.push(<Notification title="🔄 Bağlantı tekrar kuruldu" type="success" />, {
        placement: 'top-end',
      })

      if (this.currentSessionId && store.getState().auth.user) {
        const u = store.getState().auth.user
        await this.joinClass(this.currentSessionId, u.id, u.name, u.role === 'teacher', true)
      }
    })

    this.connection.onclose(async () => {
      if (this.isKicked) {
        toast.push(
          <Notification title="⚠️ Bağlantı koptu, yeniden bağlanılıyor..." type="warning" />,
          { placement: 'top-end' },
        )
        this.isConnected = false
        this.currentSessionId = undefined
        return
      }

      this.isConnected = false
      try {
        if (this.currentSessionId) {
          await this.connection.invoke('LeaveClass', this.currentSessionId)
        }
      } finally {
        this.currentSessionId = undefined
      }
    })

    this.connection.on('Error', (message: string) => {
      toast.push(<Notification title={`❌ Hata: ${message}`} type="danger" />, {
        placement: 'top-end',
      })
    })

    this.connection.on('Warning', (message: string) => {
      toast.push(<Notification title={`⚠️ Uyarı: ${message}`} type="warning" />, {
        placement: 'top-end',
      })
    })

    this.connection.on('Info', (message: string) => {
      toast.push(<Notification title={`ℹ️ Bilgi: ${message}`} type="info" />, {
        placement: 'top-end',
      })
    })

    this.connection.onreconnecting(() => {
      if (this.isKicked) {
        toast.push(
          <Notification
            title="❌ Sınıftan çıkarıldığınız için yeniden bağlanma engellendi"
            type="danger"
          />,
        )
        this.connection.stop()
        throw new Error('Reconnect blocked after kick')
      }
    })

    this.connection.on('ForceDisconnect', async (message: string) => {
      this.isKicked = true
      toast.push(<Notification title={`❌ Sınıftan çıkarıldınız: ${message}`} type="danger" />, {
        placement: 'top-end',
      })

      if (this.onForceCleanup) {
        this.onForceCleanup()
      }

      try {
        await this.connection.stop()
      } catch {}

      this.isConnected = false

      if (this.currentSessionId && store.getState().auth.user) {
        this.onParticipantLeft?.({
          userId: store.getState().auth.user.id,
          sessionId: this.currentSessionId,
          userName: store.getState().auth.user.name,
        })
      }

      this.currentSessionId = undefined
      window.location.href = ROUTES_ENUM.protected.admin.videoroom.roomList
    })
  }

  async start(): Promise<void> {
    try {
      const startPromise = this.connection.start()
      const timeout = new Promise((_, reject) =>
        setTimeout(() => reject(new Error('Bağlantı zaman aşımına uğradı')), 10000),
      )

      await Promise.race([startPromise, timeout])
      this.isConnected = true
      toast.push(<Notification title="✅ Bağlantı kuruldu" type="success" />, {
        placement: 'top-end',
      })
    } catch {
      toast.push(
        <Notification
          title="⚠️ Sunucuya bağlanılamadı. Lütfen sayfayı yenileyin veya internet bağlantınızı kontrol edin."
          type="danger"
        />,
        { placement: 'top-end' },
      )
      this.isConnected = false
    }
  }

  async joinClass(
    sessionId: string,
    userId: string,
    userName: string,
    isTeacher: boolean,
    isActive: boolean,
  ): Promise<void> {
    if (!this.isConnected) {
      toast.push(
        <Notification
          title="⚠️ Bağlantı yok. Sınıfa katılmadan önce bağlantıyı kontrol edin."
          type="warning"
        />,
      )
      return
    }

    this.currentSessionId = sessionId
    try {
      await this.connection.invoke('JoinClass', sessionId, userId, userName, isTeacher, isActive)
    } catch {
      toast.push(<Notification title="❌ Sınıfa katılamadı" type="danger" />, {
        placement: 'top-end',
      })
    }
  }

  async leaveClass(sessionId: string): Promise<void> {
    const { auth } = store.getState()

    if (!this.isConnected) {
      this.onParticipantLeft?.({ userId: auth.user.id, sessionId, userName: auth.user.name })
      return
    }

    try {
      await this.connection.invoke('LeaveClass', sessionId)
      this.currentSessionId = undefined
    } catch {
      toast.push(<Notification title="⚠️ Çıkış başarısız" type="warning" />, {
        placement: 'top-end',
      })
    }
  }

  async sendChatMessage(
    sessionId: string,
    senderId: string,
    senderName: string,
    message: string,
    isTeacher: boolean,
  ): Promise<void> {
    if (!this.isConnected) {
      const chatMessage: VideoroomChatDto = {
        id: crypto.randomUUID(),
        sessionId,
        senderId,
        senderName,
        message,
        timestamp: new Date().toISOString(),
        isTeacher,
        messageType: 'public',
      }
      setTimeout(() => {
        this.onChatMessage?.(chatMessage)
      }, 100)
      return
    }

    try {
      await this.connection.invoke(
        'SendChatMessage',
        sessionId,
        senderId,
        senderName,
        message,
        isTeacher,
        'public',
      )
    } catch {
      toast.push(<Notification title="❌ Mesaj gönderilemedi" type="danger" />, {
        placement: 'top-end',
      })
    }
  }

  async sendPrivateMessage(
    sessionId: string,
    senderId: string,
    senderName: string,
    message: string,
    recipientId: string,
    recipientName: string,
    isTeacher: boolean,
  ): Promise<void> {
    if (!this.isConnected) {
      const chatMessage: VideoroomChatDto = {
        id: crypto.randomUUID(),
        sessionId,
        senderId,
        senderName,
        message,
        timestamp: new Date().toISOString(),
        isTeacher,
        recipientId,
        recipientName,
        messageType: 'private',
      }
      setTimeout(() => {
        this.onChatMessage?.(chatMessage)
      }, 100)
      return
    }

    try {
      await this.connection.invoke(
        'SendPrivateMessage',
        sessionId,
        senderId,
        senderName,
        message,
        recipientId,
        recipientName,
        isTeacher,
        'private',
      )
    } catch {
      toast.push(<Notification title="❌ Özel mesaj gönderilemedi" type="danger" />, {
        placement: 'top-end',
      })
    }
  }

  async sendAnnouncement(
    sessionId: string,
    senderId: string,
    senderName: string,
    message: string,
    isTeacher: boolean,
  ): Promise<void> {
    if (!this.isConnected) {
      const chatMessage: VideoroomChatDto = {
        id: crypto.randomUUID(),
        sessionId,
        senderId,
        senderName,
        message,
        timestamp: new Date().toISOString(),
        isTeacher,
        messageType: 'announcement',
      }
      setTimeout(() => {
        this.onChatMessage?.(chatMessage)
      }, 100)
      return
    }

    try {
      await this.connection.invoke(
        'SendAnnouncement',
        sessionId,
        senderId,
        senderName,
        message,
        isTeacher,
      )
    } catch {
      toast.push(<Notification title="❌ Duyuru gönderilemedi" type="danger" />, {
        placement: 'top-end',
      })
    }
  }

  async muteParticipant(
    sessionId: string,
    userId: string,
    isMuted: boolean,
    isTeacher: boolean,
  ): Promise<void> {
    if (!this.isConnected) {
      setTimeout(() => {
        this.onParticipantMuted?.(userId, isMuted)
      }, 100)
      return
    }

    try {
      await this.connection.invoke('MuteParticipant', sessionId, userId, isMuted, isTeacher)
    } catch {
      toast.push(<Notification title="⚠️ Katılımcı susturulamadı" type="warning" />, {
        placement: 'top-end',
      })
    }
  }

  async raiseHand(sessionId: string, studentId: string, studentName: string): Promise<void> {
    if (!this.isConnected) {
      setTimeout(() => {
        this.onHandRaiseReceived?.(studentId)
      }, 100)
      return
    }

    try {
      await this.connection.invoke('RaiseHand', sessionId, studentId, studentName)
    } catch {
      toast.push(<Notification title="❌ El kaldırma başarısız" type="danger" />, {
        placement: 'top-end',
      })
    }
  }

  async kickParticipant(sessionId: string, participantId: string, userName: string): Promise<void> {
    if (!this.isConnected) {
      setTimeout(() => {
        this.onParticipantLeft?.({ userId: participantId, sessionId, userName })
      }, 100)
      return
    }

    try {
      await this.connection.invoke('KickParticipant', sessionId, participantId)
    } catch {
      toast.push(<Notification title="❌ Katılımcı atılamadı" type="danger" />, {
        placement: 'top-end',
      })
    }
  }

  async approveHandRaise(sessionId: string, studentId: string): Promise<void> {
    if (!this.isConnected) {
      setTimeout(() => {
        this.onHandRaiseDismissed?.(studentId)
      }, 100)
      return
    }

    try {
      await this.connection.invoke('ApproveHandRaise', sessionId, studentId)
    } catch {
      toast.push(<Notification title="⚠️ El kaldırma onayı başarısız" type="warning" />, {
        placement: 'top-end',
      })
    }
  }

  async dismissHandRaise(sessionId: string, studentId: string): Promise<void> {
    if (!this.isConnected) {
      setTimeout(() => {
        this.onHandRaiseDismissed?.(studentId)
      }, 100)
      return
    }

    try {
      await this.connection.invoke('DismissHandRaise', sessionId, studentId)
    } catch {
      toast.push(<Notification title="⚠️ El indirme başarısız" type="warning" />, {
        placement: 'top-end',
      })
    }
  }

  async sendOffer(sessionId: string, targetUserId: string, offer: RTCSessionDescriptionInit) {
    if (!this.isConnected) return
    await this.connection.invoke('SendOffer', sessionId, targetUserId, offer)
  }

  async sendAnswer(sessionId: string, targetUserId: string, answer: RTCSessionDescriptionInit) {
    if (!this.isConnected) return
    await this.connection.invoke('SendAnswer', sessionId, targetUserId, answer)
  }

  async sendIceCandidate(sessionId: string, targetUserId: string, candidate: RTCIceCandidateInit) {
    if (!this.isConnected) return
    await this.connection.invoke('SendIceCandidate', sessionId, targetUserId, candidate)
  }

  setExistingParticipantsHandler(callback: (participants: any[]) => void) {
    this.connection.on('ExistingParticipants', callback)
  }

  setAttendanceUpdatedHandler(callback: (record: VideoroomAttendanceDto) => void) {
    this.onAttendanceUpdate = callback
  }

  setParticipantJoinHandler(
    callback: (userId: string, name: string, isTeacher: boolean, isActive: boolean) => void,
  ) {
    this.onParticipantJoined = callback
  }

  setParticipantLeaveHandler(
    callback: (payload: { userId: string; sessionId: string; userName: string }) => void,
  ) {
    this.onParticipantLeft = callback
  }

  setChatMessageReceivedHandler(callback: (message: VideoroomChatDto) => void) {
    this.onChatMessage = callback
  }

  setParticipantMutedHandler(callback: (userId: string, isMuted: boolean) => void) {
    this.onParticipantMuted = callback
  }

  setHandRaiseReceivedHandler(callback: (studentId: string) => void) {
    this.onHandRaiseReceived = callback
  }

  setHandRaiseDismissedHandler(callback: (studentId: string) => void) {
    this.onHandRaiseDismissed = callback
  }

  setOfferReceivedHandler(
    callback: (fromUserId: string, offer: RTCSessionDescriptionInit) => void,
  ) {
    this.onOfferReceived = callback
  }

  setAnswerReceivedHandler(
    callback: (fromUserId: string, answer: RTCSessionDescriptionInit) => void,
  ) {
    this.onAnswerReceived = callback
  }

  setIceCandidateReceivedHandler(
    callback: (fromUserId: string, candidate: RTCIceCandidateInit) => void,
  ) {
    this.onIceCandidateReceived = callback
  }

  async disconnect(): Promise<void> {
    if (this.isConnected && this.currentSessionId) {
      try {
        await this.connection.invoke('LeaveClass', this.currentSessionId)
      } catch {
        toast.push(<Notification title="⚠️ Bağlantı koparılırken hata" type="warning" />, {
          placement: 'top-end',
        })
      }
    }
    if (this.connection) {
      await this.connection.stop()
    }
    this.isConnected = false
    this.currentSessionId = undefined
  }

  getConnectionState(): boolean {
    return this.isConnected
  }

  setForceCleanupHandler(callback: () => void) {
    this.onForceCleanup = callback
  }
}
