/**
 * <rstack-comment-bell> — Comment button with dropdown panel.
 *
 * Shows a chat-bubble icon in the header bar. Badge displays the count
 * of unresolved comment pins. Context-aware data source:
 *   - Canvas page: reads from `window.__communitySync?.doc?.commentPins`
 *   - Module pages: reads from the module-comments Automerge doc via runtime
 *
 * Listens for `comment-pins-changed` + `module-comment-pins-changed` on `window`.
 * Polls every 30s as fallback (sync may appear after component mounts).
 */

import { moduleCommentsSchema, moduleCommentsDocId } from '../module-comment-schemas';
import type { ModuleCommentPin, ModuleCommentsDoc } from '../module-comment-types';
import type { DocumentId } from '../local-first/document';

const POLL_INTERVAL = 30_000;

interface CommentMessage {
  text: string;
  createdBy: string;
  createdByName?: string;
  createdAt: number;
}

interface CommentPinData {
  messages: CommentMessage[];
  anchor?: { x: number; y: number; type?: string; elementId?: string; moduleId?: string };
  resolved?: boolean;
  createdBy?: string;
  createdByName?: string;
  createdAt?: number;
}

type PinSource = 'canvas' | 'module';

export class RStackCommentBell extends HTMLElement {
  #shadow: ShadowRoot;
  #count = 0;
  #open = false;
  #pollTimer: ReturnType<typeof setInterval> | null = null;
  #syncRef: any = null;
  #pinSource: PinSource = 'canvas';

  // Module comments state
  #moduleDocId: DocumentId | null = null;
  #runtime: any = null;
  #unsubModuleChange: (() => void) | null = null;
  #runtimePollInterval: ReturnType<typeof setInterval> | null = null;

  constructor() {
    super();
    this.#shadow = this.attachShadow({ mode: "open" });
  }

  connectedCallback() {
    this.#render();

    // Determine context: module page or canvas
    const moduleCommentsEl = document.querySelector('rstack-module-comments');
    if (moduleCommentsEl) {
      this.#pinSource = 'module';
      this.#tryConnectRuntime();
    } else {
      this.#pinSource = 'canvas';
      this.#syncRef = (window as any).__communitySync || null;
    }

    this.#refreshCount();
    this.#pollTimer = setInterval(() => this.#refreshCount(), POLL_INTERVAL);
    window.addEventListener("comment-pins-changed", this.#onPinsChanged);
    window.addEventListener("module-comment-pins-changed", this.#onPinsChanged);
    document.addEventListener("community-sync-ready", this.#onSyncReady);
  }

  disconnectedCallback() {
    if (this.#pollTimer) clearInterval(this.#pollTimer);
    if (this.#runtimePollInterval) clearInterval(this.#runtimePollInterval);
    if (this.#unsubModuleChange) this.#unsubModuleChange();
    window.removeEventListener("comment-pins-changed", this.#onPinsChanged);
    window.removeEventListener("module-comment-pins-changed", this.#onPinsChanged);
    document.removeEventListener("community-sync-ready", this.#onSyncReady);
  }

  // ── Runtime Connection (for module comments) ──

  #tryConnectRuntime() {
    const runtime = (window as any).__rspaceOfflineRuntime;
    if (runtime?.isInitialized) {
      this.#onRuntimeReady(runtime);
    } else {
      let polls = 0;
      this.#runtimePollInterval = setInterval(() => {
        const rt = (window as any).__rspaceOfflineRuntime;
        if (rt?.isInitialized) {
          clearInterval(this.#runtimePollInterval!);
          this.#runtimePollInterval = null;
          this.#onRuntimeReady(rt);
        } else if (++polls > 20) {
          clearInterval(this.#runtimePollInterval!);
          this.#runtimePollInterval = null;
        }
      }, 500);
    }
  }

  async #onRuntimeReady(runtime: any) {
    this.#runtime = runtime;
    const space = document.body?.getAttribute("data-space-slug");
    if (!space) return;

    this.#moduleDocId = moduleCommentsDocId(space);
    await runtime.subscribe(this.#moduleDocId, moduleCommentsSchema);

    this.#unsubModuleChange = runtime.onChange(this.#moduleDocId, () => {
      this.#refreshCount();
      if (this.#open) this.#render();
    });

    this.#refreshCount();
  }

  #onSyncReady = (e: Event) => {
    const sync = (e as CustomEvent).detail?.sync;
    if (sync) {
      this.#syncRef = sync;
      this.#refreshCount();
    }
  };

  #onPinsChanged = () => {
    this.#refreshCount();
    if (this.#open) this.#render();
  };

  // ── Data Access ──

  #getCurrentModuleId(): string {
    return document.body?.getAttribute("data-module-id") || "rspace";
  }

  #getModulePins(): [string, CommentPinData][] {
    if (!this.#runtime || !this.#moduleDocId) return [];
    const doc = this.#runtime.get(this.#moduleDocId) as ModuleCommentsDoc | undefined;
    if (!doc?.pins) return [];

    const moduleId = this.#getCurrentModuleId();
    const entries: [string, CommentPinData][] = [];
    for (const [pinId, pin] of Object.entries(doc.pins)) {
      if (pin.anchor.moduleId !== moduleId) continue;
      entries.push([pinId, {
        messages: (pin.messages || []).map(m => ({
          text: m.text,
          createdBy: m.authorId,
          createdByName: m.authorName,
          createdAt: m.createdAt,
        })),
        anchor: { x: 0, y: 0, type: 'element', elementId: pin.anchor.elementId, moduleId: pin.anchor.moduleId },
        resolved: pin.resolved,
        createdBy: pin.createdBy,
        createdByName: pin.createdByName,
        createdAt: pin.createdAt,
      }]);
    }
    return entries;
  }

  #getCanvasPins(): [string, CommentPinData][] {
    const sync = this.#syncRef || (window as any).__communitySync;
    const pins = sync?.doc?.commentPins;
    if (!pins) return [];
    return Object.entries(pins) as [string, CommentPinData][];
  }

  #refreshCount() {
    let pins: [string, CommentPinData][];
    if (this.#pinSource === 'module') {
      pins = this.#getModulePins();
    } else {
      const sync = this.#syncRef || (window as any).__communitySync;
      if (sync && !this.#syncRef) this.#syncRef = sync;
      pins = this.#getCanvasPins();
    }

    const newCount = pins.filter(([, p]) => !p.resolved).length;
    if (newCount !== this.#count) {
      this.#count = newCount;
      this.#updateBadge();
    }
  }

  #togglePanel() {
    this.#open = !this.#open;
    this.#render();
  }

  #getPins(): [string, CommentPinData][] {
    const entries = this.#pinSource === 'module'
      ? this.#getModulePins()
      : this.#getCanvasPins();

    // Sort by most recent message timestamp, descending
    entries.sort((a, b) => {
      const aTime = this.#latestMessageTime(a[1]);
      const bTime = this.#latestMessageTime(b[1]);
      return bTime - aTime;
    });
    return entries.slice(0, 50);
  }

  #latestMessageTime(pin: CommentPinData): number {
    if (!pin.messages || pin.messages.length === 0) return pin.createdAt || 0;
    return Math.max(...pin.messages.map(m => m.createdAt || 0));
  }

  #timeAgo(ts: number): string {
    if (!ts) return "";
    const diff = Date.now() - ts;
    const mins = Math.floor(diff / 60_000);
    if (mins < 1) return "just now";
    if (mins < 60) return `${mins}m ago`;
    const hrs = Math.floor(mins / 60);
    if (hrs < 24) return `${hrs}h ago`;
    const days = Math.floor(hrs / 24);
    return `${days}d ago`;
  }

  #esc(s: string): string {
    return s.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;");
  }

  #truncate(s: string, max = 80): string {
    if (s.length <= max) return s;
    return s.slice(0, max) + "\u2026";
  }

  #render() {
    const badge = this.#count > 0
      ? `<span class="badge">${this.#count > 99 ? "99+" : this.#count}</span>`
      : "";

    let panelHTML = "";
    if (this.#open) {
      const pins = this.#getPins();
      const header = `
        <div class="panel-header">
          <span class="panel-title">Comments</span>
          <button class="new-comment-btn" data-action="new-comment">+ New Comment</button>
        </div>
      `;

      let body: string;
      if (pins.length === 0) {
        body = `<div class="panel-empty">No comments yet</div>`;
      } else {
        body = pins.map(([pinId, pin]) => {
          const lastMsg = pin.messages?.[pin.messages.length - 1];
          const authorName = lastMsg?.createdByName || pin.createdByName || "Unknown";
          const initial = authorName.charAt(0).toUpperCase();
          const text = lastMsg?.text || "(no message)";
          const time = this.#timeAgo(this.#latestMessageTime(pin));
          const threadCount = (pin.messages?.length || 0);
          const resolvedBadge = pin.resolved
            ? `<span class="resolved-badge">Resolved</span>`
            : `<span class="open-badge">Open</span>`;

          // Show element anchor for module comments
          const anchorLabel = pin.anchor?.elementId
            ? `<span class="anchor-label">${this.#esc(pin.anchor.elementId)}</span>`
            : "";

          return `
          <div class="comment-item ${pin.resolved ? "resolved" : ""}" data-pin-id="${this.#esc(pinId)}" data-source="${this.#pinSource}" data-element-id="${this.#esc(pin.anchor?.elementId || "")}">
            <div class="comment-avatar">${initial}</div>
            <div class="comment-content">
              <div class="comment-top">
                <span class="comment-author">${this.#esc(authorName)}</span>
                ${resolvedBadge}
              </div>
              ${anchorLabel}
              <div class="comment-text">${this.#esc(this.#truncate(text))}</div>
              <div class="comment-meta">
                ${threadCount > 1 ? `<span class="thread-count">${threadCount} messages</span>` : ""}
                <span class="comment-time">${time}</span>
              </div>
            </div>
          </div>`;
        }).join("");
      }

      panelHTML = `<div class="panel">${header}<div class="panel-body">${body}</div></div>`;
    }

    this.#shadow.innerHTML = `
      <style>${STYLES}</style>
      <div class="bell-wrapper">
        <button class="comment-btn" id="comment-toggle" aria-label="Comments" title="Comments">
          <svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
            <path d="M21 11.5a8.38 8.38 0 01-.9 3.8 8.5 8.5 0 01-7.6 4.7 8.38 8.38 0 01-3.8-.9L3 21l1.9-5.7a8.38 8.38 0 01-.9-3.8 8.5 8.5 0 014.7-7.6 8.38 8.38 0 013.8-.9h.5a8.48 8.48 0 018 8v.5z"/>
          </svg>
          ${badge}
        </button>
        ${panelHTML}
      </div>
    `;

    // Toggle button
    this.#shadow.getElementById("comment-toggle")?.addEventListener("click", (e) => {
      e.stopPropagation();
      this.#togglePanel();
    });

    // Close on outside click
    if (this.#open) {
      const closeHandler = () => {
        if (this.#open) {
          this.#open = false;
          this.#render();
        }
      };
      document.addEventListener("pointerdown", closeHandler, { once: true });

      // Stop propagation from panel clicks
      this.#shadow.querySelector(".panel")?.addEventListener("pointerdown", (e) => e.stopPropagation());
    }

    // New Comment button
    this.#shadow.querySelector('[data-action="new-comment"]')?.addEventListener("click", (e) => {
      e.stopPropagation();
      this.#open = false;
      this.#render();
      window.dispatchEvent(new CustomEvent("comment-pin-activate"));
    });

    // Comment item clicks — focus the pin (context-dependent)
    this.#shadow.querySelectorAll(".comment-item").forEach((el) => {
      el.addEventListener("click", (e) => {
        e.stopPropagation();
        const pinId = (el as HTMLElement).dataset.pinId;
        const source = (el as HTMLElement).dataset.source;
        const elementId = (el as HTMLElement).dataset.elementId;
        if (!pinId) return;

        this.#open = false;
        this.#render();

        if (source === 'module') {
          window.dispatchEvent(new CustomEvent("module-comment-pin-focus", {
            detail: { pinId, elementId },
          }));
        } else {
          window.dispatchEvent(new CustomEvent("comment-pin-focus", { detail: { pinId } }));
        }
      });
    });
  }

  /** Lightweight badge update when panel is closed. */
  #updateBadge() {
    if (this.#open) {
      this.#render();
      return;
    }
    const badge = this.#shadow.querySelector(".badge") as HTMLElement | null;
    if (!badge && this.#count > 0) {
      // Need full re-render to add badge
      this.#render();
      return;
    }
    if (badge) {
      if (this.#count > 0) {
        badge.textContent = this.#count > 99 ? "99+" : String(this.#count);
        badge.style.display = "";
      } else {
        badge.style.display = "none";
      }
    }
  }

  static define(tag = "rstack-comment-bell") {
    if (!customElements.get(tag)) customElements.define(tag, RStackCommentBell);
  }
}

const STYLES = `
:host {
  display: inline-flex;
  align-items: center;
}

.bell-wrapper {
  position: relative;
}

.comment-btn {
  position: relative;
  background: none;
  border: none;
  color: var(--rs-text-muted, #94a3b8);
  cursor: pointer;
  padding: 6px;
  border-radius: 8px;
  display: flex;
  align-items: center;
  justify-content: center;
  transition: color 0.15s, background 0.15s;
}
.comment-btn:hover, .comment-btn:active {
  color: var(--rs-text-primary, #e2e8f0);
  background: var(--rs-bg-hover, rgba(255,255,255,0.05));
}

.badge {
  position: absolute;
  top: 2px;
  right: 2px;
  min-width: 16px;
  height: 16px;
  border-radius: 8px;
  background: #ef4444;
  color: white;
  font-size: 10px;
  font-weight: 700;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 0 4px;
  line-height: 1;
  pointer-events: none;
}

.panel {
  position: absolute;
  top: 100%;
  right: 0;
  margin-top: 8px;
  width: 380px;
  max-height: 480px;
  display: flex;
  flex-direction: column;
  border-radius: 10px;
  background: var(--rs-bg-surface, #1e293b);
  border: 1px solid var(--rs-border, rgba(255,255,255,0.1));
  box-shadow: var(--rs-shadow-lg, 0 8px 30px rgba(0,0,0,0.3));
  z-index: 200;
  animation: dropDown 0.15s ease;
  font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
}

@keyframes dropDown {
  from { opacity: 0; transform: translateY(-6px); }
  to { opacity: 1; transform: translateY(0); }
}

.panel-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 12px 16px;
  border-bottom: 1px solid var(--rs-border, rgba(255,255,255,0.1));
  flex-shrink: 0;
}

.panel-title {
  font-size: 0.875rem;
  font-weight: 600;
  color: var(--rs-text-primary, #e2e8f0);
}

.new-comment-btn {
  background: linear-gradient(135deg, #14b8a6, #0d9488);
  border: none;
  color: white;
  font-size: 0.75rem;
  font-weight: 600;
  padding: 5px 12px;
  border-radius: 6px;
  cursor: pointer;
  transition: opacity 0.15s;
}
.new-comment-btn:hover, .new-comment-btn:active {
  opacity: 0.85;
}

.panel-body {
  flex: 1;
  overflow-y: auto;
  touch-action: pan-y;
}

.panel-empty {
  padding: 32px 16px;
  text-align: center;
  color: var(--rs-text-muted, #94a3b8);
  font-size: 0.8rem;
}

.comment-item {
  display: flex;
  align-items: flex-start;
  gap: 10px;
  padding: 10px 16px;
  cursor: pointer;
  transition: background 0.15s;
  border-bottom: 1px solid var(--rs-border, rgba(255,255,255,0.06));
}
.comment-item:hover, .comment-item:active {
  background: var(--rs-bg-hover, rgba(255,255,255,0.05));
}
.comment-item:last-child {
  border-bottom: none;
}
.comment-item.resolved {
  opacity: 0.6;
}

.comment-avatar {
  width: 28px;
  height: 28px;
  border-radius: 50%;
  background: rgba(20,184,166,0.15);
  color: #14b8a6;
  display: flex;
  align-items: center;
  justify-content: center;
  font-weight: 700;
  font-size: 0.75rem;
  flex-shrink: 0;
  margin-top: 2px;
}

.comment-content {
  flex: 1;
  min-width: 0;
}

.comment-top {
  display: flex;
  align-items: center;
  gap: 6px;
  margin-bottom: 2px;
}

.comment-author {
  font-size: 0.8rem;
  font-weight: 600;
  color: var(--rs-text-primary, #e2e8f0);
}

.anchor-label {
  font-size: 0.65rem;
  color: var(--rs-text-muted, #64748b);
  margin-bottom: 2px;
  display: block;
}

.resolved-badge, .open-badge {
  font-size: 0.6rem;
  font-weight: 700;
  padding: 1px 5px;
  border-radius: 9999px;
  text-transform: uppercase;
  letter-spacing: 0.03em;
}
.resolved-badge {
  background: rgba(148,163,184,0.15);
  color: #94a3b8;
}
.open-badge {
  background: rgba(20,184,166,0.15);
  color: #14b8a6;
}

.comment-text {
  font-size: 0.78rem;
  color: var(--rs-text-secondary, #cbd5e1);
  line-height: 1.35;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.comment-meta {
  display: flex;
  gap: 8px;
  margin-top: 3px;
  font-size: 0.68rem;
  color: var(--rs-text-muted, #64748b);
}

.thread-count {
  font-weight: 500;
}

@media (max-width: 640px) {
  .comment-btn { display: none; }
  .panel {
    position: fixed;
    top: 60px;
    right: 8px;
    left: 8px;
    width: auto;
    max-height: calc(100vh - 80px);
  }
}
`;
