'use client'

import { useCallback, useEffect, useRef, useState } from 'react'
import {
  createClient,
  LiveClient,
  LiveTranscriptionEvents,
  type LiveTranscriptionEvent,
} from '@deepgram/sdk'

// ─────────────────────────────────────────────────────────────────────────────
// Types
// ─────────────────────────────────────────────────────────────────────────────

export type ConnectionStatus =
  | 'disconnected'
  | 'connecting'
  | 'connected'
  | 'reconnecting'
  | 'error'

export interface TranscriptWord {
  word: string
  confidence: number
  start: number
  end: number
}

export interface TranscriptResult {
  transcript: string
  isFinal: boolean
  confidence: number
  words: TranscriptWord[]
  speechFinal: boolean
}

export interface UseDeepgramStreamingProps {
  /** Callback voor transcript updates (zowel interim als final) */
  onTranscript: (result: TranscriptResult) => void
  /** Callback voor errors */
  onError?: (error: Error) => void
  /** Callback wanneer verbinding status verandert */
  onStatusChange?: (status: ConnectionStatus) => void
  /** Taal voor transcriptie (default: 'nl') */
  language?: string
  /** Deepgram model (default: 'nova-2') */
  model?: string
  /** Endpointing timeout in ms (default: 3000) */
  endpointingMs?: number
}

export interface UseDeepgramStreamingReturn {
  /** Huidige verbindingsstatus */
  status: ConnectionStatus
  /** Of er actief wordt opgenomen */
  isRecording: boolean
  /** Start opname en streaming */
  startRecording: () => Promise<void>
  /** Stop opname en streaming */
  stopRecording: () => void
  /** Pauzeer opname (behoud verbinding) */
  pauseRecording: () => void
  /** Hervat opname na pause */
  resumeRecording: () => void
  /** Of opname gepauzeerd is */
  isPaused: boolean
  /** Huidige error message (indien aanwezig) */
  error: string | null
  /** Audio analyser node voor waveform visualisatie */
  analyserNode: AnalyserNode | null
  /** Of de browser alle vereiste APIs ondersteunt */
  isBrowserSupported: boolean
}

type DeepgramAuthMode = 'accessToken' | 'apiKey'

interface DeepgramTokenResponse {
  token: string
  expiresIn: number
  authMode?: DeepgramAuthMode
}

function createTokenError(message: string) {
  const error = new Error(message)
  error.name = 'DeepgramTokenError'
  return error
}

// ─────────────────────────────────────────────────────────────────────────────
// Browser Support Check
// ─────────────────────────────────────────────────────────────────────────────

function checkBrowserSupport(): boolean {
  if (typeof window === 'undefined') return false

  const hasMediaDevices = !!(navigator.mediaDevices?.getUserMedia)
  const hasMediaRecorder = typeof MediaRecorder !== 'undefined'
  const hasAudioContext = typeof AudioContext !== 'undefined' || typeof (window as unknown as { webkitAudioContext?: typeof AudioContext }).webkitAudioContext !== 'undefined'
  const hasWebSocket = typeof WebSocket !== 'undefined'

  return hasMediaDevices && hasMediaRecorder && hasAudioContext && hasWebSocket
}

// ─────────────────────────────────────────────────────────────────────────────
// Constants
// ─────────────────────────────────────────────────────────────────────────────

const MAX_RECONNECT_ATTEMPTS = 3
const RECONNECT_BASE_DELAY_MS = 1000
const AUDIO_CHUNK_INTERVAL_MS = 250
const CONNECTION_TIMEOUT_MS = 5000

// ─────────────────────────────────────────────────────────────────────────────
// Hook Implementation
// ─────────────────────────────────────────────────────────────────────────────

export function useDeepgramStreaming({
  onTranscript,
  onError,
  onStatusChange,
  language = 'nl',
  model = 'nova-2',
  endpointingMs = 3000,
}: UseDeepgramStreamingProps): UseDeepgramStreamingReturn {
  // State
  const [status, setStatus] = useState<ConnectionStatus>('disconnected')
  const [isRecording, setIsRecording] = useState(false)
  const [isPaused, setIsPaused] = useState(false)
  const [error, setError] = useState<string | null>(null)
  const [analyserNode, setAnalyserNode] = useState<AnalyserNode | null>(null)
  const [isBrowserSupported, setIsBrowserSupported] = useState(true)

  // Check browser support on mount
  useEffect(() => {
    setIsBrowserSupported(checkBrowserSupport())
  }, [])

  // Refs
  const liveClientRef = useRef<LiveClient | null>(null)
  const mediaRecorderRef = useRef<MediaRecorder | null>(null)
  const mediaStreamRef = useRef<MediaStream | null>(null)
  const audioContextRef = useRef<AudioContext | null>(null)
  const reconnectAttemptsRef = useRef(0)
  const shouldReconnectRef = useRef(false)
  const handleReconnectRef = useRef<(() => Promise<void>) | null>(null)
  const isPausedRef = useRef(isPaused)

  // Callback refs voor stabiele referenties
  const onTranscriptRef = useRef(onTranscript)
  const onErrorRef = useRef(onError)
  const onStatusChangeRef = useRef(onStatusChange)

  useEffect(() => {
    onTranscriptRef.current = onTranscript
    onErrorRef.current = onError
    onStatusChangeRef.current = onStatusChange
  }, [onTranscript, onError, onStatusChange])

  useEffect(() => {
    isPausedRef.current = isPaused
  }, [isPaused])

  const cleanupRecordingResources = useCallback(() => {
    if (mediaRecorderRef.current && mediaRecorderRef.current.state !== 'inactive') {
      mediaRecorderRef.current.stop()
    }
    mediaRecorderRef.current = null

    if (mediaStreamRef.current) {
      mediaStreamRef.current.getTracks().forEach((track) => track.stop())
    }
    mediaStreamRef.current = null

    if (audioContextRef.current) {
      audioContextRef.current.close()
    }
    audioContextRef.current = null
    setAnalyserNode(null)

    if (liveClientRef.current) {
      liveClientRef.current.requestClose()
    }
    liveClientRef.current = null
  }, [])

  // Status update helper
  const updateStatus = useCallback((newStatus: ConnectionStatus) => {
    setStatus(newStatus)
    onStatusChangeRef.current?.(newStatus)
  }, [])

  // Fetch token van onze proxy endpoint
  const fetchToken = useCallback(async (): Promise<DeepgramTokenResponse> => {
    console.log('[Deepgram] Fetching token from /api/deepgram/token...')
    const response = await fetch('/api/deepgram/token', { method: 'POST' })
    console.log('[Deepgram] Token response status:', response.status)

    if (!response.ok) {
      const data = await response.json().catch(() => ({}))
      console.error('[Deepgram] Token fetch failed:', response.status, data)
      if (response.status === 429) {
        throw createTokenError('Rate limit bereikt. Probeer over een uur opnieuw.')
      }
      if (response.status === 401) {
        throw createTokenError('Niet ingelogd. Log opnieuw in.')
      }
      throw createTokenError(data.error || 'Kon geen token ophalen')
    }

    const data = (await response.json()) as DeepgramTokenResponse
    console.log('[Deepgram] Token received, expiresIn:', data.expiresIn, 'authMode:', data.authMode || 'accessToken')
    return data
  }, [])

  // Setup WebSocket verbinding met Deepgram
  const connect = useCallback(async () => {
    console.log('[Deepgram] connect() called')
    try {
      updateStatus('connecting')
      setError(null)

      const tokenResponse = await fetchToken()
      const authMode = tokenResponse.authMode || 'accessToken'

      console.log('[Deepgram] Creating Deepgram client with authMode:', authMode)
      const deepgram =
        authMode === 'apiKey'
          ? createClient(tokenResponse.token)
          : createClient({ accessToken: tokenResponse.token })

      console.log('[Deepgram] Creating live connection with options:', { model, language, endpointingMs })
      const connection = deepgram.listen.live({
        model,
        language,
        smart_format: true,
        interim_results: true,
        endpointing: endpointingMs,
        punctuate: true,
        utterances: true,
      })
      console.log('[Deepgram] Live connection created, setting up event handlers...')

      liveClientRef.current = connection

      await new Promise<void>((resolve, reject) => {
        let opened = false
        const timeout = window.setTimeout(() => {
          if (liveClientRef.current === connection) {
            liveClientRef.current = null
          }
          connection.requestClose()
          reject(new Error('Timeout bij verbinden met Deepgram'))
        }, CONNECTION_TIMEOUT_MS)

        // Event handlers
        connection.on(LiveTranscriptionEvents.Open, () => {
          console.log('[Deepgram] WebSocket OPEN')
          opened = true
          window.clearTimeout(timeout)
          updateStatus('connected')
          setError(null)
          resolve()
        })

        connection.on(
          LiveTranscriptionEvents.Transcript,
          (data: LiveTranscriptionEvent) => {
            const alternative = data.channel?.alternatives?.[0]
            if (!alternative) return

            const transcript = alternative.transcript || ''
            if (!transcript.trim()) return

            const words: TranscriptWord[] = (alternative.words || []).map(
              (w: { word: string; confidence: number; start: number; end: number }) => ({
                word: w.word,
                confidence: w.confidence,
                start: w.start,
                end: w.end,
              })
            )

            const result: TranscriptResult = {
              transcript,
              isFinal: data.is_final ?? false,
              confidence: alternative.confidence ?? 1,
              words,
              speechFinal: data.speech_final ?? false,
            }

            onTranscriptRef.current(result)
          }
        )

        connection.on(LiveTranscriptionEvents.Error, (err: Error) => {
          console.error('[Deepgram] WebSocket ERROR:', err)
          window.clearTimeout(timeout)
          setError(err.message || 'WebSocket fout')
          updateStatus('error')
          onErrorRef.current?.(err)

          if (!opened) {
            reject(err)
            return
          }

          // Probeer te reconnecten als we nog aan het opnemen waren
          if (shouldReconnectRef.current) {
            handleReconnectRef.current?.()
          }
        })

        connection.on(LiveTranscriptionEvents.Close, () => {
          console.log('[Deepgram] WebSocket CLOSE')
          window.clearTimeout(timeout)

          if (!opened) {
            reject(new Error('Deepgram verbinding sloot voordat deze klaar was'))
            return
          }

          if (shouldReconnectRef.current && reconnectAttemptsRef.current < MAX_RECONNECT_ATTEMPTS) {
            handleReconnectRef.current?.()
          } else {
            shouldReconnectRef.current = false
            cleanupRecordingResources()
            setIsRecording(false)
            setIsPaused(false)
            updateStatus('disconnected')
          }
        })
      })

      console.log('[Deepgram] Connection setup complete')
    } catch (err) {
      console.error('[Deepgram] Connection error:', err)
      const errorMessage = err instanceof Error ? err.message : 'Verbindingsfout'
      setError(errorMessage)
      updateStatus('error')
      onErrorRef.current?.(err instanceof Error ? err : new Error(errorMessage))

      shouldReconnectRef.current = false
      cleanupRecordingResources()
      setIsRecording(false)
      setIsPaused(false)
      throw err
    }
  }, [cleanupRecordingResources, fetchToken, model, language, endpointingMs, updateStatus])

  // Reconnect met exponential backoff
  const handleReconnect = useCallback(async () => {
    if (reconnectAttemptsRef.current >= MAX_RECONNECT_ATTEMPTS) {
      setError('Kon niet herverbinden na 3 pogingen')
      updateStatus('error')
      shouldReconnectRef.current = false
      cleanupRecordingResources()
      setIsRecording(false)
      setIsPaused(false)
      return
    }

    reconnectAttemptsRef.current++
    updateStatus('reconnecting')

    const delay = RECONNECT_BASE_DELAY_MS * Math.pow(2, reconnectAttemptsRef.current - 1)
    await new Promise((resolve) => setTimeout(resolve, delay))

    if (shouldReconnectRef.current) {
      try {
        await connect()
      } catch (err) {
        console.error('[Deepgram] Reconnect failed:', err)
        shouldReconnectRef.current = false
        cleanupRecordingResources()
        setIsRecording(false)
        setIsPaused(false)
      }
    }
  }, [cleanupRecordingResources, connect, updateStatus])

  handleReconnectRef.current = handleReconnect

  // Setup audio stream en analyser
  const setupAudio = useCallback(async () => {
    console.log('[Deepgram] setupAudio() called')
    console.log('[Deepgram] Requesting microphone access...')
    const stream = await navigator.mediaDevices.getUserMedia({ audio: true })
    console.log('[Deepgram] Microphone access granted')
    mediaStreamRef.current = stream

    // Setup Web Audio API voor waveform
    console.log('[Deepgram] Setting up AudioContext...')
    const audioContext = new AudioContext()
    const source = audioContext.createMediaStreamSource(stream)
    const analyser = audioContext.createAnalyser()
    analyser.fftSize = 256
    source.connect(analyser)

    audioContextRef.current = audioContext
    setAnalyserNode(analyser)

    // Bepaal beste mimeType (Safari ondersteunt geen audio/webm)
    const mimeType = MediaRecorder.isTypeSupported('audio/webm')
      ? 'audio/webm'
      : MediaRecorder.isTypeSupported('audio/mp4')
        ? 'audio/mp4'
        : undefined // Browser default
    console.log('[Deepgram] Using mimeType:', mimeType || 'browser default')

    // Setup MediaRecorder voor streaming naar Deepgram
    const mediaRecorder = mimeType
      ? new MediaRecorder(stream, { mimeType })
      : new MediaRecorder(stream)

    mediaRecorder.ondataavailable = (event) => {
      if (event.data.size > 0 && liveClientRef.current && !isPausedRef.current) {
        liveClientRef.current.send(event.data)
      }
    }

    console.log('[Deepgram] Starting MediaRecorder with interval:', AUDIO_CHUNK_INTERVAL_MS)
    mediaRecorder.start(AUDIO_CHUNK_INTERVAL_MS)
    mediaRecorderRef.current = mediaRecorder
    console.log('[Deepgram] Audio setup complete')
  }, [])

  // Start opname
  const startRecording = useCallback(async () => {
    console.log('[Deepgram] startRecording() called')
    try {
      setError(null)
      shouldReconnectRef.current = true
      reconnectAttemptsRef.current = 0

      // Eerst verbinden en wachten op WebSocket OPEN. Daarna pas de MediaRecorder starten,
      // zodat het eerste chunk (met WebM/EBML-header) bij Deepgram aankomt.
      console.log('[Deepgram] Step 1: Connecting to Deepgram...')
      await connect()

      console.log('[Deepgram] Step 2: Setting up audio...')
      await setupAudio()

      console.log('[Deepgram] Recording started successfully')
      setIsRecording(true)
      setIsPaused(false)
    } catch (err) {
      console.error('[Deepgram] startRecording error:', err)
      const errorMessage =
        err instanceof Error ? err.message : 'Kon opname niet starten'

      if (
        err instanceof Error &&
        (err.name === 'NotAllowedError' || err.name === 'PermissionDeniedError')
      ) {
        setError('Microfoontoegang geweigerd. Sta toegang toe in je browser.')
      } else {
        setError(errorMessage)
      }

      shouldReconnectRef.current = false
      cleanupRecordingResources()
      setIsRecording(false)
      setIsPaused(false)
      updateStatus('error')
      onErrorRef.current?.(err instanceof Error ? err : new Error(errorMessage))
    }
  }, [cleanupRecordingResources, connect, setupAudio, updateStatus])

  // Stop opname
  const stopRecording = useCallback(() => {
    shouldReconnectRef.current = false
    cleanupRecordingResources()

    setIsRecording(false)
    setIsPaused(false)
    updateStatus('disconnected')
  }, [cleanupRecordingResources, updateStatus])

  // Pauzeer opname
  const pauseRecording = useCallback(() => {
    if (mediaRecorderRef.current && mediaRecorderRef.current.state === 'recording') {
      mediaRecorderRef.current.pause()
      setIsPaused(true)
    }
  }, [])

  // Hervat opname
  const resumeRecording = useCallback(() => {
    if (mediaRecorderRef.current && mediaRecorderRef.current.state === 'paused') {
      mediaRecorderRef.current.resume()
      setIsPaused(false)
    }
  }, [])

  // Cleanup bij unmount
  useEffect(() => {
    return () => {
      shouldReconnectRef.current = false
      stopRecording()
    }
  }, [stopRecording])

  return {
    status,
    isRecording,
    startRecording,
    stopRecording,
    pauseRecording,
    resumeRecording,
    isPaused,
    error,
    analyserNode,
    isBrowserSupported,
  }
}
