<div id={@id} class="contents">
  <%!-- A board (no file): an empty canvas with the drawing tools, nothing --%>
  <%!-- else — see the moduledoc's "A board instead of a file".            --%>
  <%= if @board do %>
    <.board_canvas
      board={@board}
      viewer_canvas={@viewer_canvas}
      etcher_colors={@etcher_colors}
      etcher_line_params={@etcher_line_params}
      can_annotate={@can_annotate}
    />
  <% else %>
    <%!--                                                                          --%>
    <%!-- MediaCanvasViewer — rendered inside a parent's modal shell (a <dialog>   --%>
    <%!-- in MediaViewer, a <div class="modal"> in MediaBrowser). Owns the         --%>
    <%!-- per-file content: close button, prev/next chevrons, image canvas /      --%>
    <%!-- video / pdf / icon, composer popover, and sidebar (filename, Download,   --%>
    <%!-- metadata, comments thread).                                              --%>
    <%!--                                                                          --%>
    <%!-- All click events on the close + chevron buttons target the parent       --%>
    <%!-- LiveComponent (`phx-target={"#" <> @parent_id}`) — the parent owns      --%>
    <%!-- modal open/close state and file navigation, this component just         --%>
    <%!-- displays whichever file's been handed to it.                             --%>
    <%!--                                                                          --%>
    <%!-- No file and no (valid) board: nothing to show. --%>
    <%= if @file do %>
      <% f = @file %>
      <%!-- Close button — top-right of modal-box. Suppressed in viewer_only --%>
      <%!-- mode (standalone-page hosts provide their own back navigation).  --%>
      <button
        :if={not @viewer_only}
        type="button"
        phx-click="close_viewer"
        phx-target={"##{@parent_id}"}
        class="btn btn-ghost btn-sm btn-circle absolute top-3 right-3 z-30 bg-base-100 hover:bg-base-200 shadow-lg ring-1 ring-base-300"
        title={gettext("Close")}
      >
        <.icon name="hero-x-mark" class="w-4 h-4" />
      </button>

      <div class="flex flex-col lg:flex-row h-full overflow-hidden">
        <%!-- Viewer column (image / video / pdf / icon). Carries the         --%>
        <%!-- EtcherTooltipActions hook: Etcher re-dispatches tooltip action  --%>
        <%!-- buttons as a bubbling `etcher:tooltip-action` DOM event, and    --%>
        <%!-- this ancestor routes them — Reply to this LiveComponent, Edit   --%>
        <%!-- straight back into Etcher's label editor client-side.           --%>
        <div
          id={"pk-annotation-actions-" <> f.file_uuid}
          phx-hook="EtcherTooltipActions"
          class="flex-[7] relative flex items-center justify-center bg-base-200 overflow-hidden p-0 lg:p-2 min-h-[40vh] lg:min-h-0"
        >
          <%!-- Chevron positioning sits on a wrapper div, not the button.       --%>
          <%!-- daisyUI's active-state CSS replaces `transform` with             --%>
          <%!-- `scale(0.97)` on click, which would clobber a -translate-y-1/2    --%>
          <%!-- on the button itself and make it jump down 50% of its height.    --%>
          <%= if @has_prev do %>
            <div class="absolute left-2 top-1/2 -translate-y-1/2 z-10">
              <button
                type="button"
                phx-click="step_viewer"
                phx-value-dir="prev"
                phx-target={"##{@parent_id}"}
                class="btn btn-circle btn-ghost bg-base-100/80 hover:bg-base-100 shadow-md"
                title={gettext("Previous (←)")}
              >
                <.icon name="hero-chevron-left" class="w-5 h-5" />
              </button>
            </div>
          <% end %>
          <%= if @has_next do %>
            <div class="absolute right-2 top-1/2 -translate-y-1/2 z-10">
              <button
                type="button"
                phx-click="step_viewer"
                phx-value-dir="next"
                phx-target={"##{@parent_id}"}
                class="btn btn-circle btn-ghost bg-base-100/80 hover:bg-base-100 shadow-md"
                title={gettext("Next (→)")}
              >
                <.icon name="hero-chevron-right" class="w-5 h-5" />
              </button>
            </div>
          <% end %>

          <%= cond do %>
            <% is_binary(f.mime_type) and String.starts_with?(f.mime_type, "image/") -> %>
              <%!-- Fresco canvas + Etcher annotation layer + Tessera zoom layer.   --%>
              <%!--                                                                  --%>
              <%!-- - <Fresco.canvas> hosts the image at its base (medium)          --%>
              <%!--   resolution; the Canvas struct is built in                     --%>
              <%!--   build_viewer_canvas/2 with the file's annotations stuffed     --%>
              <%!--   into extensions.etcher — Etcher hydrates on mount via         --%>
              <%!--   handle.getExtension("etcher").                                 --%>
              <%!-- - <Etcher.layer> attaches via fresco_id; emits a single bulk    --%>
              <%!--   etcher:annotations-changed event the parent handles.          --%>
              <%!-- - <Tessera.layer> swaps medium → large → original as the user   --%>
              <%!--   zooms in, and (when urls["dzi"] is present) streams DZI tiles --%>
              <%!--   past the top raster for deep zoom on gigapixel images. Only   --%>
              <%!--   variants that exist on the file are included; tile + manifest --%>
              <%!--   fetches are lazy so unviewed regions never hit storage.       --%>
              <%= if @viewer_canvas do %>
                <Fresco.canvas
                  id={"media-zoom-" <> f.file_uuid}
                  canvas={@viewer_canvas}
                  class="w-full h-full lg:rounded"
                  theme={:inherit}
                  initial_rotation={@viewer_rotation}
                  persist_rotation={@persist_rotation}
                />
                <%!-- connectors={:off} is the HARD form (Etcher 0.13): the      --%>
                <%!-- soft default loses to the user's saved toggle, which is    --%>
                <%!-- shared across every Etcher surface in the browser — a      --%>
                <%!-- toggle flipped on a board followed people here, where the  --%>
                <%!-- anchors point at nothing (no arrow tool below). Hard off   --%>
                <%!-- also hides the toggle from the toolbar.                    --%>
                <Etcher.layer
                  fresco_id={"media-zoom-" <> f.file_uuid}
                  colors={@etcher_colors}
                  line_params={@etcher_line_params}
                  connectors={:off}
                  toolbar={@can_annotate}
                  nav_buttons={if @can_annotate, do: nil, else: [:visibility]}
                  tools={
                    if @can_annotate,
                      do: [
                        :grabber,
                        :image,
                        :rectangle,
                        :circle,
                        :polygon,
                        :freehand,
                        :marker,
                        :callout,
                        :text,
                        :dimension,
                        :line,
                        :eraser
                      ],
                      else: []
                  }
                />
                <%!-- Zoom ladder: medium → large, then for images over 4K we stream
                 DZI tiles of the original (only the visible area) instead of
                 ever loading the whole multi-MB original; for ≤4K images the
                 full original raster is the top of the ladder (no tiles). DZI
                 tiles also require the storage tile-generation setting (which
                 is what populates urls["dzi"]). --%>
                <% over_4k = max(Map.get(f, :width) || 0, Map.get(f, :height) || 0) > 4096
                has_dzi = is_binary(f.urls["dzi"]) and f.urls["dzi"] != ""
                use_tiles = over_4k and has_dzi

                tessera_sources =
                  ([{f.urls["medium"], 800}, {f.urls["large"], 1920}] ++
                     if(use_tiles,
                       do: [],
                       else: [{f.urls["original"], Map.get(f, :width) || 4096}]
                     ))
                  |> Enum.filter(fn {url, _w} -> is_binary(url) and url != "" end)
                  |> Enum.map(fn {url, width} -> %{url: url, width: width} end)

                tessera_dzi = if use_tiles, do: f.urls["dzi"], else: nil %>
                <%= if tessera_sources != [] do %>
                  <Tessera.layer
                    fresco_id={"media-zoom-" <> f.file_uuid}
                    sources={tessera_sources}
                    dzi_url={tessera_dzi}
                  />
                <% end %>
              <% end %>
            <% is_binary(f.mime_type) and String.starts_with?(f.mime_type, "video/") -> %>
              <%!-- w/h-full + object-contain, not max-w/h: max-* only caps, so a
               small-intrinsic video (or one whose metadata hasn't loaded —
               300×150 until then) rendered as a tiny box floating in the
               lightbox. Filling the column and letterboxing via
               object-contain sizes the player like the image viewer sizes
               images, whatever the source dimensions. --%>
              <video
                src={f.urls["original"]}
                controls
                class="w-full h-full object-contain rounded"
                preload="metadata"
              >
                {gettext("Your browser does not support the video tag.")}
              </video>
            <% f.mime_type == "application/pdf" -> %>
              <iframe
                src={f.urls["original"]}
                sandbox="allow-same-origin"
                class="w-full h-full rounded border-0"
                title={f.filename}
              ></iframe>
            <% audio?(f) -> %>
              <%!-- WaveformPlayer lazy-loads WaveSurfer (only when an audio file is
               open) and renders an interactive waveform driven by a single
               play/pause button. The hidden native <audio> is the playback
               source and the fallback (revealed) if the library can't load. --%>
              <%!-- max-w + mx-auto so the player sits centered as a card instead
               of the button/waveform stretching across the full lightbox
               width on large screens. --%>
              <div
                id={"waveform-" <> f.file_uuid}
                phx-hook="WaveformPlayer"
                class="flex flex-col gap-4 py-10 w-full max-w-3xl mx-auto px-8"
              >
                <div class="flex items-center gap-2">
                  <.icon name="hero-musical-note" class="w-5 h-5 text-base-content/50 shrink-0" />
                  <p class="text-sm font-semibold truncate">{f.filename}</p>
                </div>
                <div class="flex items-center gap-4">
                  <button
                    type="button"
                    data-waveform-toggle
                    class="btn btn-primary btn-circle shrink-0"
                    aria-label={gettext("Play / Pause")}
                  >
                    <span data-waveform-icon="play">
                      <.icon name="hero-play-solid" class="w-5 h-5" />
                    </span>
                    <span data-waveform-icon="pause" class="hidden">
                      <.icon name="hero-pause-solid" class="w-5 h-5" />
                    </span>
                  </button>
                  <div
                    id={"waveform-canvas-" <> f.file_uuid}
                    data-waveform
                    phx-update="ignore"
                    class="flex-1 min-h-[96px]"
                  >
                  </div>
                </div>
                <audio src={f.urls["original"]} preload="metadata" data-waveform-audio>
                  {gettext("Your browser does not support the audio tag.")}
                </audio>
              </div>
            <% true -> %>
              <div class="flex flex-col items-center justify-center text-base-content/50 py-12">
                <.icon name={file_icon(f.file_type)} class="w-32 h-32 mb-4" />
                <p class="text-2xl font-semibold">
                  {String.upcase(f.file_type || gettext("FILE"))}
                </p>
                <p class="text-sm text-base-content/70 mt-2">{f.mime_type}</p>
              </div>
          <% end %>

          <%!-- Sidebar collapse/expand toggle — pinned to the viewer's         --%>
          <%!-- top-right corner, at the seam with the info sidebar. Collapsing --%>
          <%!-- hides the sidebar so the Fresco viewer gets the full popup; the --%>
          <%!-- choice is persisted per-user (survives prev/next and reopen).   --%>
          <%!-- While collapsed (viewer spans the full popup) and on mobile     --%>
          <%!-- (panels stacked, viewer on top) the modal's close button owns   --%>
          <%!-- top-right, so the toggle shifts inward next to it (right-14).   --%>
          <button
            :if={not @viewer_only}
            type="button"
            phx-click="toggle_viewer_sidebar"
            phx-target={@myself}
            class={[
              "btn btn-ghost btn-sm btn-circle absolute top-3 z-30 bg-base-100 hover:bg-base-200 shadow-lg ring-1 ring-base-300",
              if(@sidebar_collapsed, do: "right-14", else: "right-14 lg:right-3")
            ]}
            title={
              if @sidebar_collapsed, do: gettext("Show details"), else: gettext("Hide details")
            }
            aria-label={
              if @sidebar_collapsed, do: gettext("Show details"), else: gettext("Hide details")
            }
          >
            <.icon
              name={if @sidebar_collapsed, do: "hero-chevron-left", else: "hero-chevron-right"}
              class="w-4 h-4 rotate-90 lg:rotate-0"
            />
          </button>

          <%!-- Transient rotation save-status pill — persist_rotation writes  --%>
          <%!-- are otherwise invisible (indistinguishable from a view-only     --%>
          <%!-- rotation). Shown for a moment after each actual save / failure; --%>
          <%!-- auto-hidden by the :clear_rotation_status timer.                 --%>
          <div
            :if={@rotation_status}
            class={[
              "absolute bottom-4 left-1/2 -translate-x-1/2 z-20 badge shadow-lg gap-1",
              @rotation_status == :saved && "badge-success",
              @rotation_status == :error && "badge-error"
            ]}
          >
            <.icon
              name={
                if @rotation_status == :saved, do: "hero-check", else: "hero-exclamation-triangle"
              }
              class="w-3.5 h-3.5"
            />
            {if @rotation_status == :saved,
              do: gettext("Rotation saved"),
              else: gettext("Failed to save rotation")}
          </div>

          <%!-- Floating reply popup — opened by the tooltip's Reply button    --%>
          <%!-- (the etcher:tooltip-action bridge), positioned above its shape --%>
          <%!-- by the AnnotationComposerPosition JS hook (it parses the uuid  --%>
          <%!-- from the id suffix and looks the shape up via [data-uuid]).    --%>
          <%!-- Body-only: the shape's label is its title; this box is what    --%>
          <%!-- the user wants to SAY about it, threaded under the shape's     --%>
          <%!-- master comment. --%>
          <%= if @replying_annotation_uuid && @current_user do %>
            <div
              id={"annotation-composer-popover-" <> @replying_annotation_uuid}
              phx-hook="AnnotationComposerPosition"
              class="absolute z-30 w-80 max-w-[calc(100%-1rem)] bg-base-100 rounded-lg shadow-xl border border-base-300 p-3"
              style="left: 0px; top: 0px;"
            >
              <div class="flex items-center gap-2 text-sm font-medium mb-2">
                <.icon name="hero-chat-bubble-left-right" class="w-4 h-4 text-primary" />
                <span>{gettext("Reply to this annotation")}</span>
              </div>
              <.live_component
                module={PhoenixKitWeb.Components.AnnotationComposer}
                id={"annotation-composer-" <> @replying_annotation_uuid}
                annotation_uuid={@replying_annotation_uuid}
                mode={:reply}
                parent_uuid={@reply_parent_uuid}
                file_uuid={f.file_uuid}
                current_user={@current_user}
                parent_id={@id}
                parent_module={PhoenixKitWeb.Components.MediaCanvasViewer}
              />
            </div>
          <% end %>
        </div>

        <%!-- Sidebar: filename + download + metadata + comments. Suppressed --%>
        <%!-- in viewer_only mode — standalone-page hosts render their own  --%>
        <%!-- sidebar with admin actions / metadata editor / file details —  --%>
        <%!-- and while collapsed via the viewer-corner toggle above.        --%>
        <div
          :if={not @viewer_only and not @sidebar_collapsed}
          class="flex-[3] flex flex-col gap-3 p-4 overflow-y-auto lg:min-w-[280px] bg-base-100"
        >
          <h2 class="text-base font-bold break-all leading-tight pr-10">
            {f.filename}
          </h2>

          <div class="flex flex-wrap items-center gap-2">
            <%= if f.urls["original"] do %>
              <a
                href={f.urls["original"]}
                download={f.filename}
                target="_blank"
                rel="noopener"
                class="btn btn-primary btn-sm gap-2"
              >
                <.icon name="hero-arrow-down-tray" class="w-4 h-4" /> {gettext("Download")}
              </a>
            <% end %>
            <%!-- Admin-context hosts pass details_path — full-page detail view --%>
            <%!-- with the metadata editor, variants, and storage locations.    --%>
            <.link
              :if={@details_path}
              navigate={@details_path}
              class="btn btn-ghost btn-sm gap-2"
            >
              <.icon name="hero-arrow-top-right-on-square" class="w-4 h-4" /> {gettext("Details")}
            </.link>
          </div>

          <div class="text-sm space-y-2 mt-1">
            <div>
              <span class="text-base-content/60">{gettext("Type:")}</span>
              <span class="ml-1 badge badge-ghost">
                {String.upcase(f.file_type || gettext("FILE"))}
              </span>
            </div>
            <div class="break-all">
              <span class="text-base-content/60">{gettext("MIME:")}</span>
              <span class="ml-1 font-mono text-xs">{f.mime_type}</span>
            </div>
            <div>
              <span class="text-base-content/60">{gettext("Size:")}</span>
              <span class="ml-1">{format_file_size(f.size)}</span>
            </div>
            <div>
              <span class="text-base-content/60">{gettext("Uploaded:")}</span>
              <span class="ml-1">
                {PhoenixKit.Utils.Date.format_short_datetime(f.inserted_at)}
              </span>
            </div>
          </div>

          <%!-- Comments section — gated on the Comments module being     --%>
          <%!-- installed AND enabled, plus a logged-in user. Keyed by    --%>
          <%!-- file_uuid so prev/next remounts the component cleanly.    --%>
          <%!--                                                            --%>
          <%!-- The wrapper applies a compact-mode override: shrinks the  --%>
          <%!-- comments component's typography, paddings, and avatars    --%>
          <%!-- via descendant selectors so it fits inside the sidebar    --%>
          <%!-- without truncation. The component itself isn't modified.  --%>
          <%= if comments_enabled?() and @current_user do %>
            <div class="border-t border-base-300 pt-3 mt-3 leaf-comments-compact">
              <.live_component
                module={PhoenixKitComments.Web.CommentsComponent}
                id={"media-comments-" <> f.file_uuid}
                resource_type="file"
                resource_uuid={f.file_uuid}
                current_user={@current_user}
                show_likes={true}
                title={gettext("Comments")}
                comment_decorations={build_comment_decorations(@viewer_annotations)}
                parent_module={PhoenixKitWeb.Components.MediaCanvasViewer}
                parent_id={@id}
              />
            </div>
          <% end %>
        </div>
      </div>
    <% end %>
  <% end %>
</div>
