<script>
  // SidePanel is the desktop sidebar. It has two views in the same fixed
  // panel: a list of nearby messages (default), and a message detail view
  // (image/text/echo/let go) shown when a pin or list row is selected.
  // Echo, Let go, and the back arrow all clear the selection, which
  // switches the view back to the list.
  import { mapStore } from '$lib/stores/mapStore.js';
  import { messagesStore } from '$lib/stores/messagesStore.js';
  import { getDecayInfo } from '$lib/utils/time.js';
  import { echoMessage } from '$lib/firebase/messages.js';
  import { playEchoTone } from '$lib/utils/sound.js';
  import { getPresenceText } from '$lib/utils/presence.js';
  import SharePopover from '$lib/components/SharePopover.svelte';
  import {
    incrementRead,
    incrementEchoed,
    incrementLetGo,
    hasSeenLastChancePrompt,
    markLastChancePromptSeen
  } from '$lib/utils/stats.js';

  // currently-selected message; when set, the detail view is shown instead of the list
  let { message } = $props();

  // fade-out timing for the selected message (detail view only)
  let decay = $derived(
    message ? getDecayInfo(message.createdAt, message.lastEchoAt) : null
  );

  // --- "last chance" state -------------------------------------------------
  // A message is on its genuinely LAST day if it has 1 (or 0) days left AND
  // isn't already expired/faded - daysLeft <= 1 && !isExpired. getDecayInfo()
  // derives daysLeft from lastEchoAt, so a message that was recently echoed
  // already has a much higher daysLeft and won't trigger this - "hasn't been
  // echoed recently enough to have more time" is automatically satisfied
  // whenever this condition is true.
  let isLastChance = $derived(
    decay ? (decay.daysLeft <= 1 && !decay.isExpired) : false
  );

  // one-time "last chance" prompt for the currently-open message
  let showLastChancePrompt = $state(false);

  // tracks which message was open last time this effect ran, so the
  // read-count + prompt logic below only fires once per "open" (not on
  // every reactive re-run while the same message stays selected)
  let previousMessageId = null;

  $effect(() => {
    const currentId = message?.id ?? null;

    if (currentId && currentId !== previousMessageId) {
      // --- engagement stat: a detail view was just opened -----------------
      incrementRead();

      // --- one-time "last chance" prompt -----------------------------------
      // only show it if this message is CURRENTLY in its last-chance state
      // AND this device hasn't already seen the prompt for this message ID.
      // Marking it seen immediately (rather than waiting for an explicit
      // dismiss) means it won't reappear even if the user navigates away
      // without closing it.
      if (isLastChance && !hasSeenLastChancePrompt(currentId)) {
        showLastChancePrompt = true;
        markLastChancePromptSeen(currentId);
      } else {
        showLastChancePrompt = false;
      }
    }

    if (!currentId) {
      showLastChancePrompt = false; // closed - reset for the next open
    }

    previousMessageId = currentId;
  });

  // only show the 3 most recent nearby messages, so the list view fits
  // without scrolling and the hint card stays visible underneath it
  let topMessages = $derived(
    [...$messagesStore]
      .sort((a, b) => b.createdAt.toMillis() - a.createdAt.toMillis())
      .slice(0, 3)
  );

  // ambient presence indicator ("X people have been here today"), recomputed
  // whenever the nearby-messages store updates (i.e. whenever
  // getNearbyMessages refreshes the data)
  let presenceText = $derived(getPresenceText($messagesStore));

  // back to the list view
  function close() {
    mapStore.set({ selectedMessage: null, composing: false });
  }

  async function handleEcho() {
    await echoMessage(message.id);
    // engagement stat: Echo pressed
    incrementEchoed();
    // gentle ascending "thank you" tone confirming the echo went through
    playEchoTone();
    close(); // back to the list once echoed
  }

  // close()'s behavior is unchanged (back to the list) - this only adds the
  // engagement-stat increment and prompt dismissal alongside it
  function handleLetGo() {
    incrementLetGo();
    showLastChancePrompt = false;
    close();
  }
</script>

<div class="panel">
  {#if message}
    <!-- detail view: shown for the selected message -->
    <button class="back-btn" onclick={close}>← Back</button>

    <!-- wraps both the message content and the action buttons, so the
         "last chance" gold/cream container can extend to include the
         buttons too - mirrors BottomSheet.svelte's .content wrapper, which
         already does this, for visual consistency between mobile and desktop -->
    <div class="detail-wrap" class:last-chance={isLastChance}>
      <div class="detail-content">
        {#if message.imageUrl}
          <img class="message-img" src={message.imageUrl} alt="message attachment" />
        {/if}

        <p class="message-text">{message.text}</p>

        {#if decay}
          {#if isLastChance}
            <!-- "last chance" framing replaces the normal countdown text -->
            <p class="meta last-chance-text">this is the last chance to keep this alive</p>
          {:else}
            <p class="meta">left {decay.daysAgo} days ago. fading in {decay.daysLeft} days.</p>
          {/if}
        {/if}

        {#if showLastChancePrompt}
          <!-- one-time prompt (see stats.js) - gently frames Echo as the way
               to give this message more time, without being pushy. Dismissible
               on its own, or implicitly dismissed by pressing Echo/Let go below. -->
          <div class="last-chance-prompt">
            <p>if this one means something, an echo will keep it here a little longer.</p>
            <button class="dismiss-prompt" onclick={() => showLastChancePrompt = false} aria-label="dismiss">×</button>
          </div>
        {/if}
      </div>

      <div class="actions">
        <button class="echo-button" onclick={handleEcho}>
          Echo
        </button>
        <button class="letgo-button" onclick={handleLetGo}>
          Let go
        </button>
        <!-- share link + QR code popover (see SharePopover.svelte) -->
        <SharePopover {message} />
      </div>
    </div>
  {:else}
    <!-- list view: header, compose button, nearby messages -->
    <div class="panel-header">
      <h1>Overheard: Shared memories</h1>
      <!-- ambient presence indicator replacing the old static "Messages near
           you" text - styling/font-size unchanged, only the copy is dynamic -->
      <p class="subtitle">{presenceText}</p>
    </div>

    <hr />

    <!-- compose button: opens the compose sheet, deselecting any open message -->
    <button
      class="compose-btn"
      onclick={() => mapStore.set({ selectedMessage: null, composing: true })}>
      + Leave a message here
    </button>

    <!-- message list -->
    <p class="section-label">RECENT & NEARBY</p>

    <div class="message-list">
      {#if $messagesStore.length === 0}
        <!-- shown while no messages have loaded for this area yet -->
        <div class="empty-card">
          <p>No messages nearby yet</p>
          <p class="empty-sub">Be the first to leave one</p>
        </div>
      {:else}
        {#each topMessages as msg}
          <!-- compute fade-out timing per message -->
          {@const itemDecay = getDecayInfo(msg.createdAt, msg.lastEchoAt)}
          <div
            class="message-item"
            class:selected={message?.id === msg.id}
            onclick={() => mapStore.set({ selectedMessage: msg, composing: false })}>
            <p class="msg-text">{msg.text}</p>
            <div class="msg-meta">
              <span>🕐 {itemDecay.daysAgo}d ago</span>
              <span>•</span>
              <span>{itemDecay.daysLeft}d left</span>
              <span>•</span>
              <!-- show an echo/image indicator if applicable -->
              <span>{msg.echoCount > 0 ? '🤍' : ''}{msg.imageUrl ? '🖼' : ''}</span>
            </div>
          </div>
        {/each}
      {/if}
    </div>

    <!-- hint card, always pinned to the bottom of the panel -->
    <div class="hint-card">
      <p>Messages appear as you explore</p>
      <p class="hint-sub">Tap the confetti on the map to read them</p>
    </div>

    <!-- replaces the old top-right floating archive button (desktop only -
         mobile already has an Archive tab in the bottom nav). styled to
         match .compose-btn so the two pill buttons feel like a pair -->
    <a href="/archive" class="archive-btn">
      Your archive
    </a>
  {/if}
</div>

<style>
  .panel {
    position: fixed;
    top: 0;
    left: 0;
    width: 340px;
    height: 100vh;
    /* without this, the 1.5rem/1.2rem padding below was added ON TOP of the
       100vh height, pushing the panel taller than the viewport - since the
       panel is position:fixed, that extra height just got clipped off the
       bottom, cutting off the Echo/Let go buttons */
    box-sizing: border-box;
    background: white;
    /* flat 2D style for now - shadow removed, paper-style shadows may be added later */
    display: flex;
    flex-direction: column;
    padding: 1.5rem 1.2rem;
    z-index: 100;
    gap: 0.8rem;
    font-family: sans-serif;
  }

  .panel-header h1 {
    font-size: 1.3rem;
    font-weight: 700;
    color: #111;
    margin-bottom: 0.2rem;
  }

  /* detail view */

  .back-btn {
    align-self: flex-start;
    background: none;
    border: none;
    padding: 0.4rem 0;
    font-size: 0.9rem;
    font-weight: 500;
    color: #888;
    cursor: pointer;
  }

  .back-btn:hover {
    color: #111;
  }

  .message-img {
    width: 100%;
    max-height: 220px;
    object-fit: cover;
    border-radius: 12px;
    /* flat 2D style for now - shadow removed, paper-style shadows may be added later */
  }

  .message-text {
    font-size: 0.95rem;
    line-height: 1.6;
    color: #111;
  }

  .meta {
    font-size: 0.75rem;
    color: #999;
  }

  /* wraps .detail-content and .actions together - lets the "last chance"
     background below extend around the action buttons too, not just the
     message text. flex: 1 fills the remaining height of .panel (taking over
     the role .actions's margin-top: auto used to play directly inside
     .panel), so .actions (still margin-top: auto, now relative to this
     wrapper) stays pinned to the bottom either way. */
  .detail-wrap {
    display: flex;
    flex-direction: column;
    flex: 1;

    /* neutral gray card (same palette as .hint-card below) wrapping the
       message and its action buttons together, so they read as one
       connected unit rather than floating separately - .last-chance below
       overrides this with a warm gold treatment for the same purpose. */
    background: #f9f9f9;
    border: 1px solid #eee;
    border-radius: 14px;
    padding: 0.9rem;
    margin: -0.25rem -0.25rem 0;
  }

  /* "last chance" treatment - a soft warm peach tint + border, distinct from
     the normal gray card above but staying within the existing pastel/
     parchment palette. A faint glow (box-shadow) adds a touch of warmth
     without being a jarring "alert" color like red. */
  .detail-wrap.last-chance {
    background: #fff6ea;
    border: 1px solid #f3dcb8;
    box-shadow: 0 0 0 4px rgba(243, 184, 92, 0.08);
  }

  /* replaces the normal "fading in X days" meta line when isLastChance */
  .last-chance-text {
    color: #b08a5a;
    font-weight: 500;
  }

  /* one-time prompt shown above the action buttons - same warm palette as
     .detail-wrap.last-chance, slightly more saturated so it reads as the
     "active" element within the card */
  .last-chance-prompt {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 0.5rem;
    background: #fdf0db;
    border: 1px solid #f3dcb8;
    border-radius: 10px;
    padding: 0.6rem 0.75rem;
    margin-top: 0.75rem;
    font-size: 0.8rem;
    color: #9c7240;
    line-height: 1.5;
  }

  .last-chance-prompt p {
    margin: 0;
  }

  .dismiss-prompt {
    background: none;
    border: none;
    color: #c4a37a;
    font-size: 1rem;
    line-height: 1;
    cursor: pointer;
    padding: 0;
    flex-shrink: 0;
  }

  .actions {
    display: flex;
    gap: 0.75rem;
    margin-top: auto;
  }

  .echo-button {
    flex: 1;
    padding: 0.75rem;
    background: #c4a8f5;
    color: white;
    border: none;
    border-radius: 10px;
    font-size: 0.95rem;
    font-weight: 500;
    cursor: pointer;
    transition: background 0.2s;
    /* flat 2D style for now - shadow removed, paper-style shadows may be added later */
  }

  .echo-button:hover {
    background: #b090e8;
  }

  .letgo-button {
    flex: 1;
    padding: 0.75rem;
    background: transparent;
    color: #111;
    border: 1.5px solid #ddd;
    border-radius: 10px;
    font-size: 0.95rem;
    cursor: pointer;
    /* flat 2D style for now - shadow removed, paper-style shadows may be added later */
  }

  .subtitle {
    font-size: 0.8rem;
    color: #aaa;
  }

  hr {
    border: none;
    border-top: 1px solid #eee;
    margin: 0.2rem 0;
  }

  .compose-btn {
    width: 100%;
    padding: 0.75rem;
    background: #c4a8f5;
    color: white;
    border: none;
    border-radius: 50px;
    font-size: 0.95rem;
    font-weight: 500;
    cursor: pointer;
    transition: background 0.2s;
    /* flat 2D style for now - shadow removed, paper-style shadows may be added later */
  }

  .compose-btn:hover {
    background: #b090e8;
  }

  /* same pill-button look as .compose-btn, but on an <a> tag, so it needs
     its own block/text-align/text-decoration resets to read as a button
     rather than an inline link */
  .archive-btn {
    width: 100%;
    box-sizing: border-box;
    display: block;
    text-align: center;
    text-decoration: none;
    padding: 0.75rem;
    background: #c4a8f5;
    color: white;
    border-radius: 50px;
    font-size: 0.95rem;
    font-weight: 500;
    cursor: pointer;
    transition: background 0.2s;
    /* flat 2D style for now - shadow removed, paper-style shadows may be added later */
  }

  .archive-btn:hover {
    background: #b090e8;
  }

  .section-label {
    font-size: 0.7rem;
    font-weight: 600;
    letter-spacing: 0.08em;
    color: #bbb;
    margin-top: 0.4rem;
  }

  .message-list {
    display: flex;
    flex-direction: column;
    gap: 0;
  }

  .message-item {
    padding: 0.9rem 0.5rem;
    border-bottom: 1px solid #f0f0f0;
    cursor: pointer;
    border-radius: 8px;
    transition: background 0.15s;
  }

  .message-item:hover {
    background: #fafafa;
  }

  .message-item.selected {
    background: #faf5ff;
    border-left: 3px solid #c4a8f5;
    padding-left: 0.8rem;
    /* flat 2D style for now - shadow removed, paper-style shadows may be added later */
  }

  .msg-text {
    font-size: 0.9rem;
    color: #111;
    line-height: 1.5;
    margin-bottom: 0.3rem;
    display: -webkit-box;
    -webkit-line-clamp: 2;
    -webkit-box-orient: vertical;
    overflow: hidden;
  }

  .msg-meta {
    display: flex;
    gap: 0.4rem;
    font-size: 0.75rem;
    color: #bbb;
    align-items: center;
  }

  .hint-card {
    background: #f9f9f9;
    border: 1px solid #eee;
    border-radius: 12px;
    padding: 1rem;
    text-align: center;
    font-size: 0.85rem;
    color: #999;
    /* flat 2D style for now - shadow removed, paper-style shadows may be added later */
  }

  .hint-sub {
    font-size: 0.75rem;
    color: #bbb;
    margin-top: 0.3rem;
  }

  .empty-card {
    text-align: center;
    padding: 1.5rem;
    color: #999;
    font-size: 0.85rem;
  }

  .empty-sub {
    font-size: 0.75rem;
    color: #bbb;
    margin-top: 0.3rem;
  }
</style>
