<template>
  <aside
    class="kestrel-panel-base"
    :class="inline ? 'kestrel-panel-inline' : 'kestrel-panel-overlay'"
    role="dialog"
    aria-label="Live feed"
  >
    <div class="kestrel-panel-header">
      <h2 class="font-medium tracking-wide text-kestrel-text text-shadow-glow-sm">
        {{ session?.label ?? 'Live' }}
      </h2>
      <button
        type="button"
        class="kestrel-close-btn"
        aria-label="Close panel"
        @click="$emit('close')"
      >
        <span class="text-xl leading-none">&times;</span>
      </button>
    </div>
    <div class="flex flex-1 flex-col overflow-auto p-4">
      <p class="mb-3 text-xs text-kestrel-muted">
        Live camera feed (WebRTC)
      </p>
      <div class="kestrel-video-frame">
        <video
          ref="videoRef"
          autoplay
          playsinline
          class="h-full w-full object-contain"
        />
        <div
          v-if="!hasStream && !error"
          class="absolute inset-0 flex flex-col items-center justify-center gap-2 p-4 text-center text-xs uppercase tracking-wider text-kestrel-muted"
        >
          <span>Waiting for stream…</span>
          <span
            v-if="connectionState === 'connecting'"
            class="normal-case"
          >WebRTC: connecting…</span>
          <template v-if="connectionState === 'failed'">
            <span class="normal-case font-medium text-red-400">WebRTC: failed</span>
            <p
              v-if="failureReason?.wrongHost"
              class="normal-case text-left text-amber-400"
            >
              Wrong host: server sees <strong>{{ failureReason.wrongHost.serverHostname }}</strong> but you opened this page at <strong>{{ failureReason.wrongHost.clientHostname }}</strong>. Use the same URL or set MEDIASOUP_ANNOUNCED_IP.
            </p>
            <ul class="normal-case list-inside list-disc text-left text-kestrel-muted">
              <li><strong>Firewall:</strong> Open UDP/TCP 40000-49999 on the server.</li>
              <li><strong>Wrong host:</strong> Server must see the same address you use.</li>
              <li><strong>Restrictive NAT / cellular:</strong> TURN may be required.</li>
            </ul>
          </template>
        </div>
        <div
          v-if="error"
          class="absolute inset-0 flex flex-col items-center justify-center gap-2 overflow-auto p-4 text-center text-xs uppercase tracking-wider text-red-400"
        >
          <span>Feed unavailable: {{ error }}</span>
          <template v-if="connectionState === 'failed' && failureReason">
            <p
              v-if="failureReason.wrongHost"
              class="normal-case text-left text-amber-400"
            >
              Wrong host: server sees <strong>{{ failureReason.wrongHost.serverHostname }}</strong> but you opened at <strong>{{ failureReason.wrongHost.clientHostname }}</strong>.
            </p>
            <ul class="normal-case list-inside list-disc text-left text-kestrel-muted">
              <li>Firewall: open ports 40000-49999.</li>
              <li>Wrong host: use same URL or set MEDIASOUP_ANNOUNCED_IP.</li>
              <li>Restrictive NAT: TURN may be required.</li>
            </ul>
          </template>
        </div>
      </div>
    </div>
  </aside>
</template>

<script setup>
import { createMediasoupDevice, createRecvTransport, consumeProducer, waitForConnectionState } from '~/composables/useWebRTC.js'
import { getWebRTCFailureReason } from '~/composables/useWebRTCFailureReason.js'
import { initLogger, logError, logWarn } from '~/utils/logger.js'
import { useUser } from '~/composables/useUser.js'

const { user } = useUser()

const props = defineProps({
  session: {
    type: Object,
    default: null,
  },
  /** When true, render inline (e.g. on Cameras page) instead of overlay panel */
  inline: {
    type: Boolean,
    default: false,
  },
})

defineEmits(['close'])

const videoRef = ref(null)
const hasStream = ref(false)
const error = ref('')
const connectionState = ref('') // '', 'connecting', 'connected', 'failed'
const failureReason = ref(null) // { wrongHost: { serverHostname, clientHostname } | null }
const device = ref(null)
const recvTransport = ref(null)
const consumer = ref(null)

async function runFailureReasonCheck() {
  failureReason.value = await getWebRTCFailureReason()
}

async function setupWebRTC() {
  if (!props.session?.id || !props.session?.hasStream) {
    logWarn('LiveSessionPanel: Cannot setup WebRTC', {
      hasSession: !!props.session,
      sessionId: props.session?.id,
      hasStream: props.session?.hasStream,
    })
    error.value = 'No stream available'
    return
  }

  // Initialize logger with session and user context
  initLogger(props.session.id, user.value?.id)

  try {
    error.value = ''
    hasStream.value = false
    connectionState.value = 'connecting'
    failureReason.value = null

    const rtpCapabilities = await $fetch(`/api/live/webrtc/router-rtp-capabilities?sessionId=${props.session.id}`, {
      credentials: 'include',
    })
    device.value = await createMediasoupDevice(rtpCapabilities)
    recvTransport.value = await createRecvTransport(device.value, props.session.id)

    recvTransport.value.on('connectionstatechange', () => {
      const state = recvTransport.value.connectionState
      if (state === 'connected') connectionState.value = 'connected'
      else if (state === 'failed' || state === 'disconnected' || state === 'closed') {
        logWarn('LiveSessionPanel: Receive transport connection state changed', {
          state,
          transportId: recvTransport.value.id,
          sessionId: props.session.id,
        })
        if (state === 'failed') {
          connectionState.value = 'failed'
          runFailureReasonCheck()
        }
      }
    })

    const connectionPromise = waitForConnectionState(recvTransport.value, 10000)
    consumer.value = await consumeProducer(recvTransport.value, device.value, props.session.id)
    const finalConnectionState = await connectionPromise

    if (finalConnectionState !== 'connected') {
      connectionState.value = 'failed'
      runFailureReasonCheck()
      logWarn('LiveSessionPanel: Transport not fully connected', {
        state: finalConnectionState,
        transportId: recvTransport.value.id,
        consumerId: consumer.value.id,
      })
    }
    else {
      connectionState.value = 'connected'
    }

    // 4. Attach video track to video element
    // Wait for video ref to be available (nextTick ensures DOM is updated)
    await nextTick()

    // Retry logic: wait for videoRef to become available
    let attempts = 0
    while (!videoRef.value && attempts < 10) {
      await new Promise(resolve => setTimeout(resolve, 100))
      attempts++
    }

    if (!consumer.value.track) {
      logError('LiveSessionPanel: No video track available', {
        consumerId: consumer.value.id,
        consumerKind: consumer.value.kind,
        consumerPaused: consumer.value.paused,
        consumerClosed: consumer.value.closed,
        consumerProducerId: consumer.value.producerId,
        transportConnectionState: recvTransport.value?.connectionState,
      })
      error.value = 'No video track available - consumer may not be receiving data from producer'
      return
    }

    if (!videoRef.value) {
      logError('LiveSessionPanel: Video ref not available', {
        consumerId: consumer.value.id,
        hasTrack: !!consumer.value.track,
      })
      error.value = 'Video element not available'
      return
    }

    const stream = new MediaStream([consumer.value.track])
    videoRef.value.srcObject = stream
    hasStream.value = true

    // Wait for video metadata to load (indicates video is actually receiving data)
    const metadataPromise = new Promise((resolve) => {
      if (videoRef.value.readyState >= 2) {
        resolve()
        return
      }
      let resolved = false
      const handler = () => {
        if (resolved) return
        resolved = true
        videoRef.value.removeEventListener('loadedmetadata', handler)
        if (metadataTimeoutId) clearTimeout(metadataTimeoutId)
        resolve()
      }
      videoRef.value.addEventListener('loadedmetadata', handler)
      const metadataTimeoutId = setTimeout(() => {
        if (resolved) return
        resolved = true
        videoRef.value.removeEventListener('loadedmetadata', handler)
        logWarn('LiveSessionPanel: Video metadata timeout', { consumerId: consumer.value.id })
        resolve()
      }, 5000)
    })
    await metadataPromise

    try {
      const playPromise = videoRef.value.play()
      if (playPromise !== undefined) await playPromise
    }
    catch (playErr) {
      logWarn('LiveSessionPanel: Video play() failed (may need user interaction)', {
        consumerId: consumer.value.id,
        error: playErr.message || String(playErr),
        errorName: playErr.name,
        videoPaused: videoRef.value.paused,
        videoReadyState: videoRef.value.readyState,
      })
      // Don't set error - video might still work, just needs user interaction
    }

    consumer.value.track.addEventListener('ended', () => {
      error.value = 'Video track ended'
      hasStream.value = false
    })
    videoRef.value.addEventListener('error', () => {
      logError('LiveSessionPanel: Video element error', { consumerId: consumer.value.id })
    })
  }
  catch (err) {
    connectionState.value = 'failed'
    runFailureReasonCheck()
    logError('LiveSessionPanel: WebRTC setup error', {
      err: err.message || String(err),
      data: err.data,
      stack: err.stack,
      status: err.status,
      statusCode: err.statusCode,
      sessionId: props.session?.id,
    })
    const errorMsg = err.data?.message || err.message || err.toString() || 'Failed to connect to stream'
    error.value = errorMsg
    cleanup()
  }
}

function cleanup() {
  if (consumer.value) {
    consumer.value.close()
    consumer.value = null
  }
  if (recvTransport.value) {
    recvTransport.value.close()
    recvTransport.value = null
  }
  device.value = null
  if (videoRef.value) {
    videoRef.value.srcObject = null
  }
  hasStream.value = false
  connectionState.value = ''
  failureReason.value = null
}

watch(
  () => props.session?.id,
  (id) => {
    cleanup()
    if (id && props.session?.hasStream) {
      setupWebRTC()
    }
    else {
      error.value = id ? 'No stream available' : ''
    }
  },
  { immediate: true },
)

watch(
  () => props.session?.hasStream,
  (hasStream) => {
    if (hasStream && props.session?.id && !device.value) {
      setupWebRTC()
    }
    else if (!hasStream) {
      cleanup()
      error.value = 'Stream ended'
    }
  },
)

onBeforeUnmount(() => {
  cleanup()
})
</script>
