<script setup>
import { ConnectionState, Room, RoomEvent, Track } from "livekit-client"

const toast = useToast()
const { $api } = useNuxtApp()

const status = ref(null)
const identity = ref(null)
const matrixRooms = ref([])
const matrixProjectRooms = ref([])
const matrixDirectRooms = ref([])
const activeRoomKey = ref("allgemein")
const matrixMessages = ref([])
const matrixMembers = ref([])
const matrixMessageDraft = ref("")
const matrixMessagesViewport = ref(null)
const roomCreateOpen = ref(false)
const matrixCallOpen = ref(false)
const matrixCallMode = ref("video")
const matrixCallLoading = ref(false)
const matrixCallConnected = ref(false)
const matrixCallState = ref("disconnected")
const matrixCallError = ref("")
const matrixCallSession = ref(null)
const matrixCallTiles = ref([])
const matrixCallMicEnabled = ref(true)
const matrixCallCameraEnabled = ref(true)
const matrixCallAudioContainer = ref(null)
const matrixCallScreenShareEnabled = ref(false)
const matrixCallStartedAt = ref(null)
const matrixCallDurationNow = ref(Date.now())
const matrixCallAnnouncementSent = ref(false)
const roomCreateForm = ref({
  name: "",
  key: "",
  topic: ""
})
const loading = ref(false)
const roomProvisioning = ref(false)
const roomProvisioningKey = ref("")
const roomCreating = ref(false)
const roomMembersSyncing = ref(false)
const matrixMessagesLoading = ref(false)
const matrixMembersLoading = ref(false)
const matrixMessageSending = ref(false)
const matrixAutoRefreshActive = ref(false)
const lastUpdated = ref(null)
let matrixRefreshInterval = null
let matrixCallDurationInterval = null
let matrixMessagesRequestActive = false
let matrixMembersRequestActive = false
let matrixLiveKitRoom = null
const matrixCallVideoElements = new Map()

const canUseMatrixChat = computed(() =>
  Boolean(status.value?.reachable && status.value?.provisioningConfigured)
)

const activeRoom = computed(() =>
  rooms.value.find((room) => room.key === activeRoomKey.value) || {
    key: activeRoomKey.value,
    name: activeRoomKey.value,
    description: "Mandantenweiter Austausch",
    exists: false,
    roomId: "",
    unread: 0
  }
)

const activeRoomEndpoint = computed(() =>
  `/api/communication/matrix/rooms/${encodeURIComponent(activeRoomKey.value)}`
)

const activeRoomMatrixAddress = computed(() =>
  activeRoom.value?.roomId || activeRoom.value?.alias || ""
)

const canStartMatrixCall = computed(() =>
  Boolean(canUseMatrixChat.value && activeRoom.value?.exists)
)

const matrixCallTitle = computed(() =>
  matrixCallMode.value === "audio" ? "Audioanruf" : "Videokonferenz"
)

const matrixCallStateLabel = computed(() => {
  const labels = {
    connected: "Verbunden",
    connecting: "Verbindet",
    reconnecting: "Verbindung wird wiederhergestellt",
    disconnected: "Getrennt"
  }

  return labels[String(matrixCallState.value).toLowerCase()] || matrixCallState.value
})

const matrixCallParticipantCount = computed(() =>
  new Set(matrixCallTiles.value.map((tile) => tile.identity)).size
)

const matrixCallDurationLabel = computed(() => {
  if (!matrixCallStartedAt.value) return "00:00"

  const durationSeconds = Math.max(0, Math.floor((matrixCallDurationNow.value - matrixCallStartedAt.value) / 1000))
  const minutes = String(Math.floor(durationSeconds / 60)).padStart(2, "0")
  const seconds = String(durationSeconds % 60).padStart(2, "0")

  return `${minutes}:${seconds}`
})

const roomCreateKeyPreview = computed(() =>
  normalizeRoomKey(roomCreateForm.value.key || roomCreateForm.value.name)
)

const sortRoomsByName = (roomList) => [...roomList].sort((first, second) =>
  String(first.name || "").localeCompare(String(second.name || ""), "de", { sensitivity: "base" })
)

const rooms = computed(() => {
  const baseRooms = matrixRooms.value
    .filter((room) => !["project", "direct"].includes(room.type))
    .map((room) => ({
      ...room,
      group: "Räume",
      icon: "i-heroicons-chat-bubble-left-right",
      description: room.alias || room.roomId || "Mandantenweiter Austausch"
    }))

  const projectRooms = matrixProjectRooms.value.map((room) => ({
    ...room,
    group: "Projekte",
    icon: "i-heroicons-briefcase",
    description: room.projectNumber || room.topic || "Projektkommunikation",
    provisionEndpoint: `/api/communication/matrix/project-rooms/${encodeURIComponent(room.projectId)}/provision`
  }))

  const directRooms = matrixDirectRooms.value.map((room) => ({
    ...room,
    group: "Direkt",
    icon: "i-heroicons-user-circle",
    description: room.email || room.topic || "Direktnachricht",
    provisionEndpoint: `/api/communication/matrix/direct-rooms/${encodeURIComponent(room.userId)}/provision`
  }))

  return [
    ...sortRoomsByName(baseRooms),
    ...sortRoomsByName(projectRooms),
    ...sortRoomsByName(directRooms)
  ]
})

const groupedRooms = computed(() => [
  {
    key: "rooms",
    label: "Räume",
    rooms: rooms.value.filter((room) => room.group === "Räume")
  },
  {
    key: "projects",
    label: "Projekte",
    rooms: rooms.value.filter((room) => room.group === "Projekte")
  },
  {
    key: "direct",
    label: "Direktnachrichten",
    rooms: rooms.value.filter((room) => room.group === "Direkt")
  }
].filter((group) => group.rooms.length > 0))

const normalizeRoomKey = (value) => {
  const normalized = String(value || "")
    .toLowerCase()
    .normalize("NFKD")
    .replace(/[\u0300-\u036f]/g, "")
    .replace(/ä/g, "a")
    .replace(/ö/g, "o")
    .replace(/ü/g, "u")
    .replace(/ß/g, "ss")
    .replace(/[^a-z0-9._=-]+/g, "_")
    .replace(/_+/g, "_")
    .replace(/^[._=-]+|[._=-]+$/g, "")

  return normalized || "raum"
}

const setActiveRoom = async (room) => {
  if (room.disabled || room.key === activeRoomKey.value) return

  if (!room.exists && room.provisionEndpoint) {
    await provisionRoomFromList(room)
    return
  }

  activeRoomKey.value = room.key
  matrixMessages.value = []
  matrixMembers.value = []
  await loadRoomChat({ includeMembers: true })
}

const mergeRoomIntoLists = (room) => {
  upsertRoom({ ...room, exists: true })

  if (room.type === "project") {
    matrixProjectRooms.value = matrixProjectRooms.value.map((item) =>
      item.key === room.key ? { ...item, ...room, exists: true } : item
    )
  }

  if (room.type === "direct") {
    matrixDirectRooms.value = matrixDirectRooms.value.map((item) =>
      item.key === room.key ? { ...item, ...room, exists: true } : item
    )
  }
}

const provisionRoomFromList = async (room) => {
  roomProvisioningKey.value = room.key
  try {
    const createdRoom = await $api(room.provisionEndpoint, {
      method: "POST"
    })

    mergeRoomIntoLists(createdRoom)
    activeRoomKey.value = createdRoom.key
    matrixMessages.value = []
    matrixMembers.value = []

    toast.add({
      title: "Chatraum ist bereit",
      color: "success"
    })

    await loadRoomChat({ includeMembers: true })
  } catch (error) {
    toast.add({
      title: "Chatraum konnte nicht erstellt werden",
      color: "error"
    })
  } finally {
    roomProvisioningKey.value = ""
  }
}

const upsertRoom = (room) => {
  const roomWasKnown = matrixRooms.value.some((item) => item.key === room.key)
  matrixRooms.value = roomWasKnown
    ? matrixRooms.value.map((item) => item.key === room.key ? { ...item, ...room } : item)
    : [...matrixRooms.value, room]
}

const mergeMatrixMessages = (incomingMessages) => {
  const byId = new Map()

  for (const message of matrixMessages.value) {
    byId.set(message.id, message)
  }

  for (const message of incomingMessages || []) {
    byId.set(message.id, {
      ...byId.get(message.id),
      ...message,
      pending: false,
      failed: false
    })
  }

  matrixMessages.value = Array.from(byId.values())
    .sort((a, b) => (a.timestamp || 0) - (b.timestamp || 0))
}

const scrollMessagesToBottom = async () => {
  await nextTick()
  if (!matrixMessagesViewport.value) return

  matrixMessagesViewport.value.scrollTop = matrixMessagesViewport.value.scrollHeight
}

const loadChatInfo = async () => {
  loading.value = true
  try {
    const [statusRes, identityRes, roomsRes, projectRoomsRes, directRoomsRes] = await Promise.all([
      $api("/api/communication/matrix/status"),
      $api("/api/communication/matrix/me"),
      $api("/api/communication/matrix/rooms"),
      $api("/api/communication/matrix/project-rooms"),
      $api("/api/communication/matrix/direct-rooms")
    ])

    status.value = statusRes
    identity.value = identityRes
    matrixRooms.value = roomsRes.rooms || []
    matrixProjectRooms.value = projectRoomsRes.rooms || []
    matrixDirectRooms.value = directRoomsRes.rooms || []
    lastUpdated.value = new Date()

    if (activeRoom.value?.exists && canUseMatrixChat.value) {
      await loadRoomChat({ silent: true, includeMembers: true })
    }
  } catch (error) {
    toast.add({
      title: "Chat konnte nicht geladen werden",
      color: "error"
    })
  } finally {
    loading.value = false
  }
}

const provisionActiveRoom = async () => {
  roomProvisioning.value = true
  try {
    const res = await $api(`${activeRoomEndpoint.value}/provision`, {
      method: "POST"
    })

    upsertRoom({ ...res, exists: true })

    toast.add({
      title: res.alreadyExisted ? "Chatraum ist bereit" : "Chatraum erstellt",
      color: "success"
    })

    await loadRoomChat({ includeMembers: true })
  } catch (error) {
    toast.add({
      title: "Chatraum konnte nicht erstellt werden",
      color: "error"
    })
  } finally {
    roomProvisioning.value = false
  }
}

const createRoom = async () => {
  const name = roomCreateForm.value.name.trim()
  const key = roomCreateKeyPreview.value
  const topic = roomCreateForm.value.topic.trim()

  if (!name) {
    toast.add({
      title: "Bitte gib einen Raumnamen ein",
      color: "warning"
    })
    return
  }

  roomCreating.value = true
  try {
    const room = await $api("/api/communication/matrix/rooms", {
      method: "POST",
      body: {
        key,
        name,
        topic: topic || undefined,
        type: "room"
      }
    })

    upsertRoom({ ...room, exists: true })
    activeRoomKey.value = room.key
    matrixMessages.value = []
    matrixMembers.value = []
    roomCreateForm.value = {
      name: "",
      key: "",
      topic: ""
    }
    roomCreateOpen.value = false

    toast.add({
      title: room.alreadyExisted ? "Chatraum ist bereit" : "Chatraum erstellt",
      color: "success"
    })

    await loadRoomChat({ includeMembers: true })
  } catch (error) {
    toast.add({
      title: "Chatraum konnte nicht erstellt werden",
      color: "error"
    })
  } finally {
    roomCreating.value = false
  }
}

const loadRoomMessages = async ({ silent = false } = {}) => {
  if (!canUseMatrixChat.value || !activeRoom.value?.exists) return
  if (matrixMessagesRequestActive) return

  matrixMessagesRequestActive = true
  if (!silent) matrixMessagesLoading.value = true

  try {
    const res = await $api(`${activeRoomEndpoint.value}/messages`)
    mergeMatrixMessages(res.messages || [])
    matrixRooms.value = matrixRooms.value.map((room) => room.key === activeRoomKey.value ? {
      ...room,
      alias: res.alias || room.alias,
      exists: true,
      roomId: res.roomId || room.roomId
    } : room)
    await scrollMessagesToBottom()
  } catch (error) {
    if (!silent) {
      toast.add({
        title: "Nachrichten konnten nicht geladen werden",
        color: "error"
      })
    }
  } finally {
    matrixMessagesRequestActive = false
    if (!silent) matrixMessagesLoading.value = false
  }
}

const loadRoomMembers = async ({ silent = false } = {}) => {
  if (!canUseMatrixChat.value || !activeRoom.value?.exists) return
  if (matrixMembersRequestActive) return

  matrixMembersRequestActive = true
  if (!silent) matrixMembersLoading.value = true

  try {
    const res = await $api(`${activeRoomEndpoint.value}/members`)
    matrixMembers.value = res.members || []
  } catch (error) {
    if (!silent) {
      toast.add({
        title: "Teilnehmer konnten nicht geladen werden",
        color: "error"
      })
    }
  } finally {
    matrixMembersRequestActive = false
    if (!silent) matrixMembersLoading.value = false
  }
}

const syncRoomMembers = async () => {
  if (!canUseMatrixChat.value || !activeRoom.value?.exists) return

  roomMembersSyncing.value = true
  try {
    const res = await $api(`${activeRoomEndpoint.value}/members/sync`, {
      method: "POST"
    })

    toast.add({
      title: "Teilnehmer synchronisiert",
      description: `${res.joined || 0} synchronisiert, ${res.failed || 0} fehlgeschlagen`,
      color: res.failed ? "warning" : "success"
    })

    await loadRoomMembers()
  } catch (error) {
    toast.add({
      title: "Teilnehmer konnten nicht synchronisiert werden",
      color: "error"
    })
  } finally {
    roomMembersSyncing.value = false
  }
}

const openMatrixCall = async (mode = "video") => {
  if (matrixLiveKitRoom && matrixCallConnected.value) {
    matrixCallOpen.value = true
    return
  }

  if (!canStartMatrixCall.value) {
    toast.add({
      title: "Besprechung kann noch nicht gestartet werden",
      color: "warning"
    })
    return
  }

  matrixCallMode.value = mode
  matrixCallLoading.value = true
  matrixCallError.value = ""
  matrixCallOpen.value = true
  matrixCallAnnouncementSent.value = false

  try {
    await leaveMatrixCall()
    const session = await $api(`${activeRoomEndpoint.value}/call-session`, {
      method: "POST",
      body: { mode }
    })
    matrixCallSession.value = session
    await connectMatrixCall(session, { video: mode === "video" })
    await announceMatrixCallStarted(mode)
  } catch (error) {
    matrixCallError.value = error?.data?.error || error?.message || "Verbindung fehlgeschlagen"
    toast.add({
      title: "Besprechung konnte nicht gestartet werden",
      description: matrixCallError.value,
      color: "error"
    })
  } finally {
    matrixCallLoading.value = false
  }
}

const getParticipantTiles = (participant, local = false) => {
  const publications = participant.getTrackPublications?.() || []
  const screenSharePublication = publications.find((publication) =>
    publication.kind === Track.Kind.Video &&
    publication.source === Track.Source.ScreenShare &&
    publication.track
  )
  const cameraPublication = publications.find((publication) =>
    publication.kind === Track.Kind.Video &&
    publication.source !== Track.Source.ScreenShare &&
    publication.track
  )
  const audioPublication = publications.find((publication) =>
    publication.kind === Track.Kind.Audio && publication.track
  )
  const participantId = local ? "local" : participant.sid || participant.identity
  const name = local ? "Du" : participant.name || participant.identity
  const participantTile = {
    id: `${participantId}-camera`,
    identity: participant.identity,
    name,
    local,
    speaking: participant.isSpeaking,
    screenSharing: false,
    videoTrack: cameraPublication?.track || null,
    audioTrack: audioPublication?.track || null,
    cameraEnabled: participant.isCameraEnabled,
    microphoneEnabled: participant.isMicrophoneEnabled,
  }

  if (!screenSharePublication?.track) {
    return [participantTile]
  }

  return [
    {
      id: `${participantId}-screen`,
      identity: participant.identity,
      name,
      local,
      speaking: false,
      screenSharing: true,
      videoTrack: screenSharePublication.track,
      audioTrack: null,
      cameraEnabled: participant.isCameraEnabled,
      microphoneEnabled: participant.isMicrophoneEnabled,
    },
    participantTile
  ]
}

const attachMatrixCallMedia = async () => {
  await nextTick()

  for (const tile of matrixCallTiles.value) {
    const videoElement = matrixCallVideoElements.get(tile.id)

    if (videoElement && tile.videoTrack) {
      tile.videoTrack.attach(videoElement)
    }

    if (!tile.local && tile.audioTrack && matrixCallAudioContainer.value) {
      const audioElement = tile.audioTrack.attach()
      audioElement.autoplay = true
      audioElement.dataset.participant = tile.id
      matrixCallAudioContainer.value.appendChild(audioElement)
    }
  }
}

const refreshMatrixCallTiles = async () => {
  if (!matrixLiveKitRoom) {
    matrixCallTiles.value = []
    return
  }

  matrixCallAudioContainer.value?.replaceChildren()
  matrixCallTiles.value = [
    ...getParticipantTiles(matrixLiveKitRoom.localParticipant, true),
    ...Array.from(matrixLiveKitRoom.remoteParticipants.values()).flatMap((participant) =>
      getParticipantTiles(participant)
    )
  ].sort((a, b) => Number(b.screenSharing) - Number(a.screenSharing))

  await attachMatrixCallMedia()
}

const startMatrixCallDurationTimer = () => {
  matrixCallStartedAt.value = Date.now()
  matrixCallDurationNow.value = Date.now()

  if (matrixCallDurationInterval) {
    window.clearInterval(matrixCallDurationInterval)
  }

  matrixCallDurationInterval = window.setInterval(() => {
    matrixCallDurationNow.value = Date.now()
  }, 1000)
}

const stopMatrixCallDurationTimer = () => {
  if (matrixCallDurationInterval) {
    window.clearInterval(matrixCallDurationInterval)
    matrixCallDurationInterval = null
  }

  matrixCallStartedAt.value = null
  matrixCallDurationNow.value = Date.now()
}

const announceMatrixCallStarted = async (mode) => {
  if (matrixCallAnnouncementSent.value || !activeRoom.value?.exists) return

  matrixCallAnnouncementSent.value = true

  try {
    const message = await $api(`${activeRoomEndpoint.value}/messages`, {
      method: "POST",
      body: {
        text: `${mode === "audio" ? "Audioanruf" : "Videokonferenz"} gestartet.`
      }
    })

    mergeMatrixMessages([message])
    await scrollMessagesToBottom()
  } catch (error) {
    matrixCallAnnouncementSent.value = false
  }
}

const connectMatrixCall = async (session, { video = true } = {}) => {
  const room = new Room({
    adaptiveStream: true,
    dynacast: true,
  })

  matrixLiveKitRoom = room
  matrixCallState.value = ConnectionState.Connecting

  const refreshEvents = [
    RoomEvent.TrackSubscribed,
    RoomEvent.TrackUnsubscribed,
    RoomEvent.LocalTrackPublished,
    RoomEvent.LocalTrackUnpublished,
    RoomEvent.ParticipantConnected,
    RoomEvent.ParticipantDisconnected,
    RoomEvent.TrackMuted,
    RoomEvent.TrackUnmuted,
    RoomEvent.ActiveSpeakersChanged,
    RoomEvent.LocalTrackSubscribed,
  ]

  for (const eventName of refreshEvents) {
    room.on(eventName, refreshMatrixCallTiles)
  }

  room.on(RoomEvent.ConnectionStateChanged, (state) => {
    matrixCallState.value = state
    matrixCallConnected.value = state === ConnectionState.Connected
    if (state === ConnectionState.Connected && !matrixCallStartedAt.value) {
      startMatrixCallDurationTimer()
    }
  })

  room.on(RoomEvent.Disconnected, () => {
    matrixCallConnected.value = false
    matrixCallState.value = ConnectionState.Disconnected
    matrixCallScreenShareEnabled.value = false
    stopMatrixCallDurationTimer()
  })

  await room.connect(session.liveKitUrl, session.liveKitToken)
  await room.localParticipant.setMicrophoneEnabled(true)
  await room.localParticipant.setCameraEnabled(video)
  matrixCallMicEnabled.value = true
  matrixCallCameraEnabled.value = video
  matrixCallScreenShareEnabled.value = false
  if (!matrixCallStartedAt.value) {
    startMatrixCallDurationTimer()
  }
  await refreshMatrixCallTiles()
}

const leaveMatrixCall = async () => {
  if (matrixLiveKitRoom) {
    matrixLiveKitRoom.disconnect()
    matrixLiveKitRoom = null
  }

  matrixCallVideoElements.clear()
  matrixCallAudioContainer.value?.replaceChildren()
  matrixCallTiles.value = []
  matrixCallConnected.value = false
  matrixCallState.value = "disconnected"
  matrixCallScreenShareEnabled.value = false
  matrixCallAnnouncementSent.value = false
  stopMatrixCallDurationTimer()
}

const closeMatrixCall = async () => {
  await leaveMatrixCall()
  matrixCallOpen.value = false
}

const minimizeMatrixCall = () => {
  matrixCallOpen.value = false
}

const setMatrixCallVideoElement = (tileId, element) => {
  if (element) {
    matrixCallVideoElements.set(tileId, element)
    const tile = matrixCallTiles.value.find((item) => item.id === tileId)
    tile?.videoTrack?.attach(element)
  } else {
    matrixCallVideoElements.delete(tileId)
  }
}

const toggleMatrixCallMic = async () => {
  if (!matrixLiveKitRoom) return

  const enabled = !matrixCallMicEnabled.value
  await matrixLiveKitRoom.localParticipant.setMicrophoneEnabled(enabled)
  matrixCallMicEnabled.value = enabled
  await refreshMatrixCallTiles()
}

const toggleMatrixCallCamera = async () => {
  if (!matrixLiveKitRoom) return

  const enabled = !matrixCallCameraEnabled.value
  await matrixLiveKitRoom.localParticipant.setCameraEnabled(enabled)
  matrixCallCameraEnabled.value = enabled
  await refreshMatrixCallTiles()
}

const toggleMatrixCallScreenShare = async () => {
  if (!matrixLiveKitRoom) return

  const enabled = !matrixCallScreenShareEnabled.value
  try {
    await matrixLiveKitRoom.localParticipant.setScreenShareEnabled(enabled)
    matrixCallScreenShareEnabled.value = enabled
    await refreshMatrixCallTiles()
  } catch (error) {
    toast.add({
      title: "Bildschirmfreigabe konnte nicht geändert werden",
      color: "error"
    })
  }
}

const loadRoomChat = async ({ silent = false, includeMembers = false } = {}) => {
  await loadRoomMessages({ silent })

  if (includeMembers) {
    await loadRoomMembers({ silent })
  }
}

const sendMatrixMessage = async () => {
  const text = matrixMessageDraft.value.trim()
  if (!text || !canUseMatrixChat.value || !activeRoom.value?.exists) return

  const optimisticId = `pending-${Date.now()}`
  const optimisticMessage = {
    id: optimisticId,
    sender: identity.value?.matrixUserId || "Du",
    senderDisplayName: identity.value?.displayName || "Du",
    body: text,
    timestamp: Date.now(),
    own: true,
    pending: true,
    failed: false
  }

  matrixMessages.value = [
    ...matrixMessages.value,
    optimisticMessage
  ]
  matrixMessageDraft.value = ""
  await scrollMessagesToBottom()

  matrixMessageSending.value = true
  try {
    const message = await $api(`${activeRoomEndpoint.value}/messages`, {
      method: "POST",
      body: { text }
    })

    matrixMessages.value = matrixMessages.value.map((item) =>
      item.id === optimisticId ? message : item
    )
  } catch (error) {
    matrixMessages.value = matrixMessages.value.map((item) =>
      item.id === optimisticId ? { ...item, pending: false, failed: true } : item
    )
    toast.add({
      title: "Nachricht konnte nicht gesendet werden",
      color: "error"
    })
  } finally {
    matrixMessageSending.value = false
  }
}

const startMatrixAutoRefresh = () => {
  if (matrixRefreshInterval) return

  matrixAutoRefreshActive.value = true
  matrixRefreshInterval = window.setInterval(() => {
    if (!document.hidden && canUseMatrixChat.value && activeRoom.value?.exists) {
      loadRoomChat({ silent: true })
    }
  }, 15000)
}

const stopMatrixAutoRefresh = () => {
  if (!matrixRefreshInterval) return

  window.clearInterval(matrixRefreshInterval)
  matrixRefreshInterval = null
  matrixAutoRefreshActive.value = false
}

const formatMessageTime = (timestamp) => {
  if (!timestamp) return ""

  return new Intl.DateTimeFormat("de-DE", {
    dateStyle: "short",
    timeStyle: "short"
  }).format(new Date(timestamp))
}

const formatLastUpdated = computed(() => {
  if (!lastUpdated.value) return "Noch nicht aktualisiert"

  return new Intl.DateTimeFormat("de-DE", {
    hour: "2-digit",
    minute: "2-digit"
  }).format(lastUpdated.value)
})

watch(
  () => matrixMessages.value.length,
  () => scrollMessagesToBottom()
)

onMounted(async () => {
  await loadChatInfo()
  startMatrixAutoRefresh()
})

onBeforeUnmount(() => {
  stopMatrixAutoRefresh()
  stopMatrixCallDurationTimer()
  leaveMatrixCall()
})
</script>

<template>
  <div class="flex min-h-0 flex-1 overflow-hidden">
    <aside class="hidden w-72 shrink-0 flex-col border-r border-default bg-default lg:flex">
      <div class="border-b border-default p-4">
        <div class="flex items-center justify-between gap-3">
          <div>
            <h1 class="text-lg font-semibold text-highlighted">
              Chat
            </h1>
            <p class="text-xs text-muted">
              {{ formatLastUpdated }}
            </p>
          </div>
          <UButton
              icon="i-heroicons-arrow-path"
              color="neutral"
              variant="ghost"
              :loading="loading || matrixMessagesLoading"
              :disabled="!canUseMatrixChat"
              @click="loadRoomChat({ includeMembers: true })"
          />
        </div>
      </div>

      <div class="border-b border-default p-3">
        <UButton
            icon="i-heroicons-plus"
            color="primary"
            variant="soft"
            block
            :disabled="!canUseMatrixChat"
            @click="roomCreateOpen = !roomCreateOpen"
        >
          Raum erstellen
        </UButton>

        <form
            v-if="roomCreateOpen"
            class="mt-3 space-y-3 rounded-md border border-default bg-muted p-3"
            @submit.prevent="createRoom"
        >
          <UInput
              v-model="roomCreateForm.name"
              placeholder="Raumname"
              :disabled="roomCreating"
          />
          <UInput
              v-model="roomCreateForm.key"
              placeholder="Raum-Key optional"
              :disabled="roomCreating"
          />
          <UTextarea
              v-model="roomCreateForm.topic"
              placeholder="Thema optional"
              :rows="2"
              :disabled="roomCreating"
          />
          <p class="truncate text-xs text-muted">
            Key: {{ roomCreateKeyPreview }}
          </p>
          <div class="flex justify-end gap-2">
            <UButton
                type="button"
                color="neutral"
                variant="ghost"
                :disabled="roomCreating"
                @click="roomCreateOpen = false"
            >
              Abbrechen
            </UButton>
            <UButton
                type="submit"
                icon="i-heroicons-check"
                :loading="roomCreating"
                :disabled="!roomCreateForm.name.trim()"
            >
              Erstellen
            </UButton>
          </div>
        </form>
      </div>

      <div class="flex-1 space-y-4 overflow-y-auto p-3">
        <section
            v-for="group in groupedRooms"
            :key="group.key"
            class="space-y-1"
        >
          <div class="flex items-center justify-between px-2">
            <p class="text-[11px] font-semibold uppercase tracking-wide text-muted">
              {{ group.label }}
            </p>
            <span class="text-[11px] text-muted">{{ group.rooms.length }}</span>
          </div>
          <button
              v-for="room in group.rooms"
              :key="room.key"
              type="button"
              class="flex w-full items-center gap-3 rounded-md px-3 py-2 text-left transition"
              :class="[
                room.disabled ? 'cursor-not-allowed opacity-50' : 'text-highlighted hover:bg-muted',
                room.key === activeRoomKey ? 'bg-muted ring-1 ring-primary/20' : ''
              ]"
              :disabled="room.disabled"
              @click="setActiveRoom(room)"
          >
            <span class="flex size-9 shrink-0 items-center justify-center rounded-md bg-primary/10 text-primary">
              <UIcon :name="room.icon || 'i-heroicons-chat-bubble-left-right'" class="size-5" />
            </span>
            <span class="min-w-0 flex-1">
              <span class="block truncate text-sm font-medium">{{ room.name }}</span>
              <span class="block truncate text-xs text-muted">{{ room.description }}</span>
            </span>
            <UBadge
                v-if="roomProvisioningKey === room.key"
                color="primary"
                variant="soft"
                size="xs"
            >
              lädt
            </UBadge>
            <UBadge
                v-else-if="room.exists"
                color="success"
                variant="soft"
                size="xs"
            >
              aktiv
            </UBadge>
            <UBadge
                v-else-if="!room.disabled"
                color="neutral"
                variant="soft"
                size="xs"
            >
              offen
            </UBadge>
          </button>
        </section>
      </div>
    </aside>

    <main class="flex min-w-0 flex-1 flex-col bg-muted">
      <header class="flex shrink-0 items-center justify-between gap-3 border-b border-default bg-default px-4 py-3">
        <div class="min-w-0">
          <div class="flex items-center gap-2">
            <h2 class="truncate text-base font-semibold text-highlighted">
              {{ activeRoom.name }}
            </h2>
            <UBadge
                v-if="matrixAutoRefreshActive && activeRoom?.exists"
                color="success"
                variant="soft"
                size="xs"
            >
              Live
            </UBadge>
          </div>
          <p class="truncate text-xs text-muted">
            <span v-if="matrixMembers.length">{{ matrixMembers.length }} Teilnehmer</span>
            <span v-else>{{ activeRoom.description }}</span>
          </p>
        </div>

        <div class="flex items-center gap-2">
          <UButton
              icon="i-heroicons-phone"
              color="neutral"
              variant="outline"
              aria-label="Audioanruf starten"
              :loading="matrixCallLoading && matrixCallMode === 'audio'"
              :disabled="!canStartMatrixCall"
              @click="openMatrixCall('audio')"
          />
          <UButton
              icon="i-heroicons-video-camera"
              color="primary"
              variant="soft"
              aria-label="Videokonferenz starten"
              :loading="matrixCallLoading && matrixCallMode === 'video'"
              :disabled="!canStartMatrixCall"
              @click="openMatrixCall('video')"
          />
          <UButton
              class="lg:hidden"
              icon="i-heroicons-plus"
              color="primary"
              variant="soft"
              :disabled="!canUseMatrixChat"
              @click="roomCreateOpen = !roomCreateOpen"
          />
          <UButton
              icon="i-heroicons-arrow-path"
              color="neutral"
              variant="outline"
              :loading="matrixMessagesLoading"
              :disabled="!canUseMatrixChat || !activeRoom?.exists"
              @click="loadRoomChat({ includeMembers: true })"
          >
            Laden
          </UButton>
        </div>
      </header>

      <div class="border-b border-default bg-default p-3 lg:hidden">
        <div class="flex gap-2 overflow-x-auto pb-1">
          <button
              v-for="room in rooms"
              :key="room.key"
              type="button"
              class="shrink-0 rounded-md border border-default px-3 py-2 text-left text-sm"
              :class="room.key === activeRoomKey ? 'bg-muted text-highlighted' : 'text-muted'"
              :disabled="room.disabled"
              @click="setActiveRoom(room)"
          >
            {{ room.name }}
          </button>
        </div>

        <form
            v-if="roomCreateOpen"
            class="mt-3 grid gap-2 rounded-md border border-default bg-muted p-3"
            @submit.prevent="createRoom"
        >
          <UInput
              v-model="roomCreateForm.name"
              placeholder="Raumname"
              :disabled="roomCreating"
          />
          <UInput
              v-model="roomCreateForm.key"
              placeholder="Raum-Key optional"
              :disabled="roomCreating"
          />
          <UTextarea
              v-model="roomCreateForm.topic"
              placeholder="Thema optional"
              :rows="2"
              :disabled="roomCreating"
          />
          <p class="truncate text-xs text-muted">
            Key: {{ roomCreateKeyPreview }}
          </p>
          <div class="flex justify-end gap-2">
            <UButton
                type="button"
                color="neutral"
                variant="ghost"
                :disabled="roomCreating"
                @click="roomCreateOpen = false"
            >
              Abbrechen
            </UButton>
            <UButton
                type="submit"
                icon="i-heroicons-check"
                :loading="roomCreating"
                :disabled="!roomCreateForm.name.trim()"
            >
              Erstellen
            </UButton>
          </div>
        </form>
      </div>

      <div
          v-if="status && (!status.reachable || !status.provisioningConfigured)"
          class="border-b border-default bg-default px-4 py-3"
      >
        <UAlert
            icon="i-heroicons-exclamation-triangle"
            color="warning"
            variant="soft"
            title="Matrix ist noch nicht bereit"
            description="Bitte prüfe das Matrix-Setup, bevor der Chat genutzt wird."
        />
      </div>

      <div
          v-else-if="status && !activeRoom?.exists"
          class="border-b border-default bg-default px-4 py-3"
      >
        <UAlert
            icon="i-heroicons-chat-bubble-left-right"
            color="primary"
            variant="soft"
            :title="`${activeRoom.name} ist noch nicht in Matrix angelegt`"
            description="Die Raum-Metadaten sind in FEDEO vorhanden. Lege jetzt den Matrix-Raum dazu an."
        >
          <template #actions>
            <UButton
                icon="i-heroicons-plus"
                :loading="roomProvisioning"
                @click="provisionActiveRoom"
            >
              Matrix-Raum erstellen
            </UButton>
          </template>
        </UAlert>
      </div>

      <div
          v-if="matrixLiveKitRoom && matrixCallConnected"
          class="flex shrink-0 flex-wrap items-center justify-between gap-3 border-b border-primary/20 bg-primary/10 px-4 py-3"
      >
        <div class="min-w-0">
          <p class="truncate text-sm font-medium text-highlighted">
            {{ matrixCallTitle }} läuft in {{ activeRoom.name }}
          </p>
          <p class="truncate text-xs text-muted">
            {{ matrixCallParticipantCount }} Teilnehmer · {{ matrixCallDurationLabel }} · {{ matrixCallStateLabel }}
          </p>
        </div>
        <div class="flex items-center gap-2">
          <UButton
              icon="i-heroicons-arrow-top-right-on-square"
              color="primary"
              variant="soft"
              size="sm"
              @click="matrixCallOpen = true"
          >
            Öffnen
          </UButton>
          <UButton
              icon="i-heroicons-phone-x-mark"
              color="error"
              variant="soft"
              size="sm"
              @click="closeMatrixCall"
          >
            Auflegen
          </UButton>
        </div>
      </div>

      <div
          ref="matrixMessagesViewport"
          class="min-h-0 flex-1 space-y-3 overflow-y-auto p-4 sm:p-5"
      >
        <div
            v-if="!matrixMessages.length && !matrixMessagesLoading"
            class="flex h-full min-h-64 items-center justify-center text-sm text-muted"
        >
          Noch keine Nachrichten in diesem Chat.
        </div>

        <div
            v-for="message in matrixMessages"
            :key="message.id"
            class="flex"
            :class="message.own ? 'justify-end' : 'justify-start'"
        >
          <div
              class="max-w-[82%] rounded-lg px-3 py-2 shadow-sm sm:max-w-[68%]"
              :class="[
                message.own ? 'bg-primary text-inverted' : 'bg-default text-highlighted',
                message.pending ? 'opacity-70' : '',
                message.failed ? 'bg-error text-inverted' : ''
              ]"
          >
            <div class="mb-1 flex flex-wrap items-center gap-x-2 gap-y-1 text-[11px] opacity-75">
              <span class="truncate font-medium">
                {{ message.own ? "Du" : message.senderDisplayName || message.sender }}
              </span>
              <span>{{ formatMessageTime(message.timestamp) }}</span>
              <span v-if="message.pending">wird gesendet</span>
              <span v-if="message.failed">nicht gesendet</span>
            </div>
            <p class="whitespace-pre-wrap break-words text-sm">
              {{ message.body }}
            </p>
          </div>
        </div>
      </div>

      <form
          class="flex shrink-0 gap-2 border-t border-default bg-default p-3"
          @submit.prevent="sendMatrixMessage"
      >
        <UInput
            v-model="matrixMessageDraft"
            class="min-w-0 flex-1"
            placeholder="Nachricht schreiben"
            :disabled="matrixMessageSending || !canUseMatrixChat || !activeRoom?.exists"
            @keydown.enter.exact.prevent="sendMatrixMessage"
        />
        <UButton
            type="submit"
            icon="i-heroicons-paper-airplane"
            :loading="matrixMessageSending"
            :disabled="!matrixMessageDraft.trim() || !canUseMatrixChat || !activeRoom?.exists"
        >
          Senden
        </UButton>
      </form>
    </main>

    <aside class="hidden w-80 shrink-0 border-l border-default bg-default xl:block">
      <div class="border-b border-default p-4">
        <h3 class="text-sm font-semibold text-highlighted">
          Raumdetails
        </h3>
        <p class="mt-1 break-all font-mono text-xs text-muted">
          {{ activeRoom?.roomId || activeRoom?.alias || "Noch kein Matrix-Raum" }}
        </p>
      </div>

      <div class="space-y-5 p-4">
        <div>
          <div class="mb-2 flex items-center justify-between gap-2">
            <h4 class="text-xs font-medium uppercase text-muted">
              Teilnehmer
            </h4>
            <div class="flex items-center gap-1">
              <UButton
                  icon="i-heroicons-user-plus"
                  color="neutral"
                  variant="ghost"
                  size="xs"
                  :loading="roomMembersSyncing"
                  :disabled="!canUseMatrixChat || !activeRoom?.exists"
                  @click="syncRoomMembers"
              />
              <UButton
                  icon="i-heroicons-arrow-path"
                  color="neutral"
                  variant="ghost"
                  size="xs"
                  :loading="matrixMembersLoading"
                  :disabled="!canUseMatrixChat || !activeRoom?.exists"
                  @click="loadRoomMembers"
              />
            </div>
          </div>

          <div class="space-y-2">
            <div
                v-for="member in matrixMembers"
                :key="member.matrixUserId"
                class="flex items-center gap-2"
            >
              <span class="flex size-8 shrink-0 items-center justify-center rounded-md bg-muted text-xs font-medium text-highlighted">
                {{ (member.displayName || member.matrixUserId || "?").slice(0, 1).toUpperCase() }}
              </span>
              <div class="min-w-0">
                <p class="truncate text-sm font-medium text-highlighted">
                  {{ member.displayName || member.matrixUserId }}
                </p>
                <p class="truncate text-xs text-muted">
                  {{ member.matrixUserId }}
                </p>
              </div>
            </div>

            <p
                v-if="!matrixMembers.length"
                class="text-sm text-muted"
            >
              Noch keine Teilnehmer geladen.
            </p>
          </div>
        </div>

        <div>
          <h4 class="mb-2 text-xs font-medium uppercase text-muted">
            Besprechung
          </h4>
          <div class="grid grid-cols-2 gap-2">
            <UButton
                icon="i-heroicons-phone"
                color="neutral"
                variant="outline"
                block
                :loading="matrixCallLoading && matrixCallMode === 'audio'"
                :disabled="!canStartMatrixCall"
                @click="openMatrixCall('audio')"
            >
              Audio
            </UButton>
            <UButton
                icon="i-heroicons-video-camera"
                color="primary"
                variant="soft"
                block
                :loading="matrixCallLoading && matrixCallMode === 'video'"
                :disabled="!canStartMatrixCall"
                @click="openMatrixCall('video')"
            >
              Video
            </UButton>
          </div>
        </div>

        <div>
          <h4 class="mb-2 text-xs font-medium uppercase text-muted">
            Eigene Identität
          </h4>
          <p class="break-all font-mono text-xs text-highlighted">
            {{ identity?.matrixUserId || "-" }}
          </p>
        </div>
      </div>
    </aside>

    <UModal
        v-model:open="matrixCallOpen"
        fullscreen
        class="h-[100dvh]"
    >
      <template #content>
        <div class="flex h-[100dvh] flex-col bg-default">
          <header class="flex shrink-0 items-center justify-between gap-3 border-b border-default px-4 py-3">
            <div class="min-w-0">
              <h3 class="truncate text-base font-semibold text-highlighted">
                {{ matrixCallTitle }} · {{ activeRoom.name }}
              </h3>
              <p class="truncate text-xs text-muted">
                {{ activeRoomMatrixAddress }}
              </p>
            </div>
            <div class="flex items-center gap-2">
              <UButton
                  icon="i-heroicons-microphone"
                  color="neutral"
                  :variant="matrixCallMicEnabled ? 'soft' : 'outline'"
                  :disabled="!matrixLiveKitRoom"
                  @click="toggleMatrixCallMic"
              >
                {{ matrixCallMicEnabled ? "Mikro an" : "Mikro aus" }}
              </UButton>
              <UButton
                  icon="i-heroicons-video-camera"
                  color="neutral"
                  :variant="matrixCallCameraEnabled ? 'soft' : 'outline'"
                  :disabled="!matrixLiveKitRoom"
                  @click="toggleMatrixCallCamera"
              >
                {{ matrixCallCameraEnabled ? "Kamera an" : "Kamera aus" }}
              </UButton>
              <UButton
                  icon="i-heroicons-computer-desktop"
                  color="neutral"
                  :variant="matrixCallScreenShareEnabled ? 'soft' : 'outline'"
                  :disabled="!matrixLiveKitRoom"
                  @click="toggleMatrixCallScreenShare"
              >
                {{ matrixCallScreenShareEnabled ? "Freigabe an" : "Freigeben" }}
              </UButton>
              <UButton
                  icon="i-heroicons-arrows-pointing-in"
                  color="neutral"
                  variant="ghost"
                  :disabled="!matrixLiveKitRoom"
                  @click="minimizeMatrixCall"
              >
                Minimieren
              </UButton>
              <UButton
                  icon="i-heroicons-phone-x-mark"
                  color="error"
                  variant="soft"
                  @click="closeMatrixCall"
              >
                Auflegen
              </UButton>
              <UButton
                  icon="i-heroicons-x-mark"
                  color="neutral"
                  variant="ghost"
                  @click="closeMatrixCall"
              />
            </div>
          </header>

          <div class="min-h-0 flex-1 bg-muted">
            <div
                v-if="matrixCallLoading"
                class="flex h-full items-center justify-center text-sm text-muted"
            >
              Besprechung wird gestartet...
            </div>
            <div
                v-else-if="matrixCallError"
                class="flex h-full items-center justify-center p-6"
            >
              <UAlert
                  icon="i-heroicons-exclamation-triangle"
                  color="error"
                  variant="soft"
                  title="Besprechung konnte nicht gestartet werden"
                  :description="matrixCallError"
              />
            </div>
            <div
                v-else-if="canStartMatrixCall"
                class="flex h-full flex-col"
            >
              <div class="flex shrink-0 items-center justify-between border-b border-default bg-default px-4 py-2 text-xs text-muted">
                <span>{{ matrixCallParticipantCount }} Teilnehmer · {{ matrixCallDurationLabel }}</span>
                <span>{{ matrixCallStateLabel }}</span>
              </div>
              <div class="grid min-h-0 flex-1 auto-rows-fr gap-3 overflow-y-auto p-3 sm:grid-cols-2 xl:grid-cols-3">
                <div
                    v-for="tile in matrixCallTiles"
                    :key="tile.id"
                    class="relative min-h-52 overflow-hidden rounded-lg bg-inverted text-inverted"
                    :class="tile.speaking ? 'ring-2 ring-primary' : ''"
                >
                  <video
                      v-if="tile.videoTrack"
                      :ref="(element) => setMatrixCallVideoElement(tile.id, element)"
                      class="h-full w-full"
                      :class="tile.screenSharing ? 'object-contain bg-black' : 'object-cover'"
                      autoplay
                      playsinline
                      :muted="tile.local"
                  />
                  <div
                      v-else
                      class="flex h-full min-h-52 items-center justify-center bg-elevated text-highlighted"
                  >
                    <span class="flex size-16 items-center justify-center rounded-lg bg-primary/10 text-xl font-semibold text-primary">
                      {{ (tile.name || tile.identity || "?").slice(0, 1).toUpperCase() }}
                    </span>
                  </div>
                  <div class="absolute inset-x-0 bottom-0 flex items-center justify-between bg-black/55 px-3 py-2 text-sm text-white">
                    <span class="truncate">
                      {{ tile.name }}
                      <span v-if="tile.screenSharing" class="ml-1 text-xs opacity-80">teilt Bildschirm</span>
                    </span>
                    <div class="flex items-center gap-2">
                      <UIcon
                          v-if="tile.screenSharing"
                          name="i-heroicons-computer-desktop"
                          class="size-4"
                      />
                      <UIcon
                          v-if="!tile.screenSharing"
                          :name="tile.microphoneEnabled ? 'i-heroicons-microphone' : 'i-heroicons-microphone'"
                          class="size-4"
                          :class="tile.microphoneEnabled ? 'opacity-100' : 'opacity-35'"
                      />
                      <UIcon
                          v-if="!tile.screenSharing"
                          :name="tile.cameraEnabled ? 'i-heroicons-video-camera' : 'i-heroicons-video-camera-slash'"
                          class="size-4"
                      />
                    </div>
                  </div>
                </div>
              </div>
              <div ref="matrixCallAudioContainer" class="hidden" />
            </div>
            <div
                v-else
                class="flex h-full items-center justify-center p-6"
            >
              <UAlert
                  icon="i-heroicons-exclamation-triangle"
                  color="warning"
                  variant="soft"
                  title="Besprechung nicht verfügbar"
                  description="Der Matrix-Raum oder LiveKit ist noch nicht bereit."
              />
            </div>
          </div>
        </div>
      </template>
    </UModal>
  </div>
</template>
