<script lang="ts">
  import cx from "classnames"
  import {Track} from "livekit-client"
  import {displayProfileByPubkey, loadProfile} from "@welshman/app"
  import Pin from "@assets/icons/pin.svg?dataurl"
  import Button from "@lib/components/Button.svelte"
  import Icon from "@lib/components/Icon.svelte"
  import ProfileCircle from "@app/components/ProfileCircle.svelte"
  import VideoCallVideo from "@app/components/VideoCallVideo.svelte"
  import {
    currentVoiceSession,
    currentVoiceRoom,
    videoCallContentActive,
    videoCallLayoutRevision,
    videoPrimaryTileKey,
    toggleVideoPrimaryTile,
    pubkeyFromLiveKitIdentity,
  } from "@app/voice"

  type Variant = "mobile" | "desktop-split" | "desktop-full"

  type Props = {
    variant: Variant
    url: string
    h: string
    visible?: boolean
    class?: string
  }

  type Tile = {
    identity: string
    isLocal: boolean
    trackSid: string
    attachable: Track | undefined
    source: Track.Source.Camera | Track.Source.ScreenShare
  }

  type TileLayout = "spotlight" | "default" | "strip"

  const {variant, url, h, visible = true, class: className = ""}: Props = $props()

  const roomMatches = $derived($currentVoiceRoom?.url === url && $currentVoiceRoom?.h === h)

  const allowEmptyPanel = $derived(variant === "desktop-split" || variant === "desktop-full")

  const showPanel = $derived(
    visible &&
      roomMatches &&
      (variant === "mobile" ? $videoCallContentActive : $videoCallContentActive || allowEmptyPanel),
  )

  const tiles = $derived.by(() => {
    // eslint-disable-next-line @typescript-eslint/no-unused-expressions -- re-run when remote video subscribes
    $videoCallLayoutRevision
    const session = $currentVoiceSession
    if (!session || $currentVoiceRoom?.url !== url || $currentVoiceRoom?.h !== h) {
      return []
    }

    const room = session.room
    const out: Tile[] = []
    const lp = room.localParticipant

    if (session.cameraOn) {
      const localPub = lp.getTrackPublication(Track.Source.Camera)
      out.push({
        identity: lp.identity,
        isLocal: true,
        trackSid: localPub?.trackSid ?? "local-camera",
        attachable: localPub?.track,
        source: Track.Source.Camera,
      })
    }

    if (session.screenShareOn) {
      const localPub = lp.getTrackPublication(Track.Source.ScreenShare)
      out.push({
        identity: lp.identity,
        isLocal: true,
        trackSid: localPub?.trackSid ?? "local-screen",
        attachable: localPub?.track,
        source: Track.Source.ScreenShare,
      })
    }

    for (const rp of room.remoteParticipants.values()) {
      const camPub = rp.getTrackPublication(Track.Source.Camera)
      if (camPub?.isSubscribed && camPub.track) {
        out.push({
          identity: rp.identity,
          isLocal: false,
          trackSid: camPub.trackSid,
          attachable: camPub.track,
          source: Track.Source.Camera,
        })
      }
      const screenPub = rp.getTrackPublication(Track.Source.ScreenShare)
      if (screenPub?.isSubscribed && screenPub.track) {
        out.push({
          identity: rp.identity,
          isLocal: false,
          trackSid: screenPub.trackSid,
          attachable: screenPub.track,
          source: Track.Source.ScreenShare,
        })
      }
    }

    return out
  })

  /** Identity + source only — LiveKit can change trackSid after publish, which broke spotlight + stale-key effect. */
  const tileKey = (t: Tile) => `${t.identity}\x1f${t.source}`

  const primaryTile = $derived.by(() => {
    const k = $videoPrimaryTileKey
    if (k === undefined) return undefined
    return tiles.find(t => tileKey(t) === k)
  })

  const secondaryTiles = $derived.by(() => {
    const p = primaryTile
    if (p === undefined) return tiles
    const pk = tileKey(p)
    return tiles.filter(t => tileKey(t) !== pk)
  })

  const useSpotlightLayout = $derived(primaryTile !== undefined)
  const useMultiGrid = $derived(!useSpotlightLayout && tiles.length > 2)

  $effect(() => {
    const k = $videoPrimaryTileKey
    if (k === undefined) return
    if (!tiles.some(t => tileKey(t) === k)) {
      videoPrimaryTileKey.set(undefined)
    }
  })

  $effect(() => {
    for (const t of tiles) {
      const pk = pubkeyFromLiveKitIdentity(t.identity)
      if (pk) loadProfile(pk)
    }
  })

  const labelFor = (identity: string, source: Tile["source"]) => {
    const pk = pubkeyFromLiveKitIdentity(identity)
    const name = pk ? displayProfileByPubkey(pk) : "Unknown"
    return source === Track.Source.ScreenShare ? `${name} · screen` : name
  }

  const showTileGrid = $derived(tiles.length > 0)

  const spotlightHandlerFor = (key: string) => () => {
    toggleVideoPrimaryTile(key)
  }

  const panelChrome = $derived(
    cx(
      variant === "mobile" &&
        "cb ct cw z-compose bg-base-300/95 fixed inset-x-0 flex min-h-0 flex-col gap-2 overflow-hidden p-2 md:hidden",
      variant === "desktop-split" &&
        "cb ct cw-split-video z-compose bg-base-300/95 fixed hidden min-h-0 flex-col gap-2 overflow-hidden p-2 md:flex",
      variant === "desktop-full" &&
        "cb ct cw z-compose bg-base-300/95 fixed hidden min-h-0 flex-col gap-2 overflow-hidden p-2 md:flex",
      className,
    ),
  )
</script>

{#snippet videoTile(tile: Tile, layout: TileLayout)}
  <div
    class={cx(
      "relative isolate overflow-hidden rounded-box shadow-sm",
      layout === "spotlight" && "min-h-0 flex-1",
      layout === "default" && "aspect-video w-full min-h-0",
      layout === "strip" && "aspect-video w-44 shrink-0",
      tile.source === Track.Source.ScreenShare ? "bg-black" : "bg-base-100",
      $videoPrimaryTileKey === tileKey(tile) &&
        "ring-2 ring-primary ring-offset-2 ring-offset-base-300",
    )}>
    {#if tile.attachable}
      <VideoCallVideo
        track={tile.attachable}
        muted={tile.isLocal}
        fit={tile.source === Track.Source.ScreenShare ? "contain" : "cover"}
        class="pointer-events-none absolute inset-0" />
    {:else}
      <div class="absolute inset-0 flex items-center justify-center">
        <ProfileCircle pubkey={pubkeyFromLiveKitIdentity(tile.identity)} {url} size={14} />
      </div>
    {/if}
    <span
      class="pointer-events-none absolute bottom-1 left-1 max-w-[calc(100%-0.5rem)] truncate rounded bg-base-100/80 px-1.5 py-0.5 text-xs">
      {labelFor(tile.identity, tile.source)}{tile.isLocal ? " (you)" : ""}
    </span>
    {#if tiles.length > 1}
      <Button
        data-tip={$videoPrimaryTileKey === tileKey(tile) ? "Exit spotlight" : "Spotlight"}
        class="absolute right-1 top-1 z-20 btn btn-xs btn-circle btn-ghost bg-base-100/70"
        onclick={spotlightHandlerFor(tileKey(tile))}>
        <Icon icon={Pin} size={3} />
      </Button>
    {/if}
  </div>
{/snippet}

{#if showPanel && (showTileGrid || allowEmptyPanel)}
  <div class={panelChrome}>
    {#if showTileGrid}
      {#if useSpotlightLayout && primaryTile}
        <div class="flex min-h-0 flex-1 flex-col gap-2 overflow-hidden">
          {@render videoTile(primaryTile, "spotlight")}
          {#if secondaryTiles.length > 0}
            <div
              class="flex max-h-40 shrink-0 flex-row gap-2 overflow-x-auto overflow-y-hidden py-0.5">
              {#each secondaryTiles as tile (tileKey(tile))}
                {@render videoTile(tile, "strip")}
              {/each}
            </div>
          {/if}
        </div>
      {:else if useMultiGrid}
        <div
          class="grid min-h-0 flex-1 auto-rows-fr grid-cols-1 gap-2 overflow-y-auto sm:grid-cols-2">
          {#each tiles as tile (tileKey(tile))}
            {@render videoTile(tile, "default")}
          {/each}
        </div>
      {:else}
        <div class="flex min-h-0 flex-1 flex-col gap-2 overflow-y-auto">
          {#each tiles as tile (tileKey(tile))}
            {@render videoTile(tile, "default")}
          {/each}
        </div>
      {/if}
    {:else}
      <div
        class="flex min-h-[12rem] flex-1 flex-col items-center justify-center gap-2 rounded-box bg-base-100/50 p-4 text-center text-sm opacity-80">
        <p>No camera or screen share yet.</p>
        <p class="text-xs">
          Use the camera or screen share control in the voice widget to share video.
        </p>
      </div>
    {/if}
  </div>
{/if}
