// ── Connections Panel ───────────────────────────────────────────────────────
// Manages connection status display for Slack, Email, and Teams channels.
// Queries gateway for channel health and displays status with notifications.
// ─────────────────────────────────────────────────────────────────────────────

"use strict";

(function () {
  const CHANNELS = {
    slack: {
      id: "slack",
      name: "Slack",
      configPath: "channels.slack",
      icon: `<svg viewBox="0 0 24 24" fill="currentColor">
        <path d="M5.042 15.165a2.528 2.528 0 0 1-2.52 2.523A2.528 2.528 0 0 1 0 15.165a2.527 2.527 0 0 1 2.522-2.52h2.52v2.52zm1.271 0a2.527 2.527 0 0 1 2.521-2.52 2.527 2.527 0 0 1 2.521 2.52v6.313A2.528 2.528 0 0 1 8.834 24a2.528 2.528 0 0 1-2.521-2.522v-6.313zM8.834 5.042a2.528 2.528 0 0 1-2.521-2.52A2.528 2.528 0 0 1 8.834 0a2.528 2.528 0 0 1 2.521 2.522v2.52H8.834zm0 1.271a2.528 2.528 0 0 1 2.521 2.521 2.528 2.528 0 0 1-2.521 2.521H2.522A2.528 2.528 0 0 1 0 8.834a2.528 2.528 0 0 1 2.522-2.521h6.312zm10.124 2.521a2.528 2.528 0 0 1 2.52-2.521A2.528 2.528 0 0 1 24 8.834a2.528 2.528 0 0 1-2.522 2.521h-2.52V8.834zm-1.271 0a2.528 2.528 0 0 1-2.521 2.521 2.528 2.528 0 0 1-2.521-2.521V2.522A2.528 2.528 0 0 1 15.166 0a2.528 2.528 0 0 1 2.521 2.522v6.312zm-2.521 10.124a2.528 2.528 0 0 1 2.521 2.52A2.528 2.528 0 0 1 15.166 24a2.528 2.528 0 0 1-2.521-2.522v-2.52h2.521zm0-1.271a2.528 2.528 0 0 1-2.521-2.521 2.528 2.528 0 0 1 2.521-2.521h6.312A2.528 2.528 0 0 1 24 15.166a2.528 2.528 0 0 1-2.522 2.521h-6.312z"/>
      </svg>`,
      setupInfo: "Configure Slack integration with Bot and App tokens for real-time messaging.",
      configExample: `channels:
  slack:
    botToken: "xoxb-..."
    appToken: "xapp-..."`,
    },
    email: {
      id: "email",
      name: "Email",
      configPath: "hooks.gmail + outlook plugin",
      icon: `<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
        <rect x="2" y="4" width="20" height="16" rx="2"/>
        <path d="M22 6l-10 7L2 6"/>
      </svg>`,
      // setupInfo/configExample unused for email — the modal renders two
      // per-provider sections (Outlook, Gmail) instead. See openModal().
      setupInfo: "",
      configExample: "",
    },
    msteams: {
      id: "msteams",
      name: "Microsoft Teams",
      configPath: "channels.msteams",
      icon: `<svg viewBox="0 0 24 24" fill="currentColor">
        <path d="M19.19 8.77c-.73 0-1.39.29-1.88.76V8.5a2.5 2.5 0 0 0-2.5-2.5h-1.62c.18-.46.28-.96.28-1.48a4.04 4.04 0 0 0-4.04-4.04c-2.23 0-4.04 1.81-4.04 4.04 0 .52.1 1.02.28 1.48H4.5A2.5 2.5 0 0 0 2 8.5v9a2.5 2.5 0 0 0 2.5 2.5h9a2.5 2.5 0 0 0 2.5-2.5v-.97c.49.47 1.15.76 1.88.76a2.69 2.69 0 0 0 2.69-2.69v-3.14a2.69 2.69 0 0 0-2.69-2.69h.31zM9.43 2.98a2.04 2.04 0 1 1 0 4.08 2.04 2.04 0 0 1 0-4.08zm4.57 14.52a.5.5 0 0 1-.5.5h-9a.5.5 0 0 1-.5-.5v-9a.5.5 0 0 1 .5-.5h9a.5.5 0 0 1 .5.5v9zm5.88-4.17a.69.69 0 0 1-.69.69.69.69 0 0 1-.69-.69v-3.14c0-.38.31-.69.69-.69.38 0 .69.31.69.69v3.14z"/>
        <circle cx="17.5" cy="5.5" r="2.5"/>
      </svg>`,
      setupInfo: "Connect Microsoft Teams using Bot Framework for bidirectional messaging.",
      configExample: `channels:
  msteams:
    appId: "..."
    appPassword: "..."
    tenantId: "..."`,
    },
  };

  const state = {
    slack: { status: "checking", notifications: 0 },
    email: { status: "checking", notifications: 0, gmail: null, outlook: null },
    msteams: { status: "checking", notifications: 0 },
  };

  // ── DOM Elements ─────────────────────────────────────────────────────────
  const elements = {
    slack: {
      btn: document.getElementById("conn-slack"),
      status: document.getElementById("conn-slack-status"),
      state: document.getElementById("conn-slack-state"),
      notif: document.getElementById("conn-slack-notif"),
    },
    email: {
      btn: document.getElementById("conn-email"),
      status: document.getElementById("conn-email-status"),
      state: document.getElementById("conn-email-state"),
      notif: document.getElementById("conn-email-notif"),
    },
    msteams: {
      btn: document.getElementById("conn-teams"),
      status: document.getElementById("conn-teams-status"),
      state: document.getElementById("conn-teams-state"),
      notif: document.getElementById("conn-teams-notif"),
    },
  };

  // ── Update UI ────────────────────────────────────────────────────────────
  function updateChannelUI(channelId) {
    const el = elements[channelId];
    const s = state[channelId];
    if (!el || !el.btn) {
      return;
    }

    // Set data-state attribute for CSS styling
    el.btn.setAttribute("data-state", s.status);

    // Update status text
    const statusText = {
      checking: "Checking...",
      connected: "Connected",
      disconnected: "Not configured",
      error: "Connection error",
      pending: "Connecting...",
    };
    el.status.textContent = statusText[s.status] || s.status;

    // Update notification badge
    if (s.notifications > 0) {
      el.notif.textContent = s.notifications > 99 ? "99+" : s.notifications;
    } else {
      el.notif.textContent = "";
    }
  }

  // ── Check Channel Status ─────────────────────────────────────────────────
  async function checkChannelStatus() {
    if (!window.gateway?.connected) {
      // Gateway not connected, mark all as pending
      Object.keys(state).forEach((id) => {
        state[id].status = "pending";
        updateChannelUI(id);
      });
      return;
    }

    try {
      // Query gateway for channel status using channels.status RPC
      const result = await window.gateway.rpc("channels.status", { probe: true });
      const channels = result?.channels ?? {};

      // Update Slack status
      if (channels.slack) {
        const isConnected = channels.slack.configured && channels.slack.connected !== false;
        state.slack.status = isConnected ? "connected" : "disconnected";
        state.slack.notifications = channels.slack.unread ?? 0;
      } else {
        state.slack.status = "disconnected";
      }

      // Update Email status: connected if EITHER Outlook OR Gmail is live.
      // Outlook is injected into channels.outlook by the gateway
      // (src/gateway/server-methods/channels.ts).
      const gmail = channels.gmail || channels.email || null;
      const outlook = channels.outlook || null;
      const gmailConnected = !!(gmail && gmail.configured && gmail.connected !== false);
      const outlookConnected = !!(outlook && outlook.configured && outlook.connected !== false);
      state.email.gmail = gmail;
      state.email.outlook = outlook;
      state.email.status = gmailConnected || outlookConnected ? "connected" : "disconnected";
      state.email.notifications = (gmail?.unread ?? 0) + (outlook?.unread ?? 0);

      // Update Teams status
      if (channels.msteams) {
        const isConnected = channels.msteams.configured && channels.msteams.connected !== false;
        state.msteams.status = isConnected ? "connected" : "disconnected";
        state.msteams.notifications = channels.msteams.unread ?? 0;
      } else {
        state.msteams.status = "disconnected";
      }
    } catch (err) {
      console.warn("[connections] Failed to fetch channel status:", err);
      // Mark all as disconnected on error
      Object.keys(state).forEach((id) => {
        state[id].status = "disconnected";
      });
    }

    // Update all UI
    Object.keys(state).forEach(updateChannelUI);
  }

  // ── Modal Elements ────────────────────────────────────────────────────────
  const modalOverlay = document.getElementById("conn-modal-overlay");
  const modalIcon = document.getElementById("conn-modal-icon");
  const modalTitle = document.getElementById("conn-modal-title");
  const modalBody = document.getElementById("conn-modal-body");
  const modalDocs = document.getElementById("conn-modal-docs");
  const modalClose = document.getElementById("conn-modal-close");

  // Docs viewer (in-app scrollable markdown renderer, stacks above conn modal)
  const docsViewerOverlay = document.getElementById("docs-viewer-overlay");
  const docsViewerBody = document.getElementById("docs-viewer-body");
  const docsViewerTitle = document.getElementById("docs-viewer-title");
  const docsViewerClose = document.getElementById("docs-viewer-close");

  // Tracks which channel's connection modal is currently open, so the
  // "View Documentation" button knows which docs key to fetch.
  let currentModalChannelId = null;

  const DOCS_VIEWER_TITLES = {
    email: "Email — Outlook & Gmail",
    slack: "Slack Setup",
    msteams: "Microsoft Teams Setup",
  };

  function stripFrontmatter(markdown) {
    if (!markdown.startsWith("---\n") && !markdown.startsWith("---\r\n")) {
      return markdown;
    }
    const end = markdown.indexOf("\n---", 4);
    if (end === -1) {
      return markdown;
    }
    return markdown.slice(end + 4).replace(/^\s+/, "");
  }

  async function openDocsViewer(key) {
    if (!docsViewerOverlay || !docsViewerBody) {
      return;
    }
    docsViewerTitle.textContent = DOCS_VIEWER_TITLES[key] || "Documentation";
    docsViewerBody.innerHTML = '<p class="docs-viewer-loading">Loading documentation…</p>';
    docsViewerOverlay.classList.add("open");
    docsViewerOverlay.setAttribute("aria-hidden", "false");
    // Capture phase so we beat the conn-modal's Esc handler
    document.addEventListener("keydown", onDocsViewerKey, true);

    if (!window.gateway?.rpc) {
      docsViewerBody.innerHTML =
        '<p class="docs-viewer-error">Gateway is not connected — cannot load documentation.</p>';
      return;
    }
    try {
      const result = await window.gateway.rpc("docs.get", { key });
      const markdown = result && typeof result.markdown === "string" ? result.markdown : "";
      if (!markdown) {
        docsViewerBody.innerHTML =
          '<p class="docs-viewer-error">Documentation is empty or missing on this install.</p>';
        return;
      }
      const body = stripFrontmatter(markdown);
      const html =
        window.marked && typeof window.marked.parse === "function"
          ? window.marked.parse(body)
          : "<pre>" + escHtml(body) + "</pre>";
      docsViewerBody.innerHTML = html;
      // Make external links open in a new tab with safe rel
      docsViewerBody.querySelectorAll('a[href^="http"]').forEach((a) => {
        a.setAttribute("target", "_blank");
        a.setAttribute("rel", "noopener noreferrer");
      });
      docsViewerBody.scrollTop = 0;
    } catch (err) {
      const msg = err && err.message ? err.message : String(err);
      docsViewerBody.innerHTML =
        '<p class="docs-viewer-error">Failed to load documentation: ' + escHtml(msg) + "</p>";
    }
  }

  function closeDocsViewer() {
    if (!docsViewerOverlay) {
      return;
    }
    docsViewerOverlay.classList.remove("open");
    docsViewerOverlay.setAttribute("aria-hidden", "true");
    document.removeEventListener("keydown", onDocsViewerKey, true);
  }

  function onDocsViewerKey(e) {
    if (e.key === "Escape") {
      // Stop the conn-modal's keydown listener from also firing
      e.stopImmediatePropagation();
      closeDocsViewer();
    }
  }

  function renderProviderSection(title, connected, detailLine, actionLine) {
    const klass = connected ? "connected" : "disconnected";
    const badge = connected ? "CONNECTED" : "NOT CONFIGURED";
    return `
      <div class="conn-modal-status ${klass}" style="margin-bottom:6px;">
        <div class="conn-modal-status-dot"></div>
        <span class="conn-modal-status-text"><strong>${escHtml(title)}</strong> — ${escHtml(detailLine)}</span>
        <span class="conn-modal-status-badge">${badge}</span>
      </div>
      <p class="conn-modal-info" style="margin-top:0;margin-bottom:14px;">${actionLine}</p>
    `;
  }

  function renderEmailModalBody(channelState) {
    const outlook = channelState.outlook;
    const gmail = channelState.gmail;
    const outlookConnected = !!(outlook && outlook.configured && outlook.connected !== false);
    const gmailConnected = !!(gmail && gmail.configured && gmail.connected !== false);

    const outlookDetail = outlookConnected
      ? `Connected as ${outlook.email || "Microsoft account"}`
      : "Microsoft 365 / outlook.com / hotmail.com";
    const outlookAction = outlookConnected
      ? `Type <code>/outlook logout</code> in chat to disconnect.`
      : `Type <code>/outlook login</code> in chat — a Microsoft device-code flow will start and tools become available immediately after sign-in.`;

    const gmailAccount = gmail && typeof gmail.account === "string" ? gmail.account : null;
    const gmailDetail = gmailConnected
      ? `Watching ${gmailAccount || "your Gmail inbox"}`
      : "Gmail / Google Workspace (requires Pub/Sub setup)";
    const gmailAction = gmailConnected
      ? `Incoming email creates <code>hook:gmail:&lt;id&gt;</code> sessions automatically.`
      : `Configure <code>hooks.gmail</code> in <code>symi.json</code> — see the docs link below for the full Google Cloud Pub/Sub walkthrough.`;

    return (
      renderProviderSection("Outlook 365", outlookConnected, outlookDetail, outlookAction) +
      renderProviderSection("Gmail", gmailConnected, gmailDetail, gmailAction)
    );
  }

  function openModal(channelId) {
    const channel = CHANNELS[channelId];
    const channelState = state[channelId];
    if (!channel) {
      return;
    }

    // Set icon
    modalIcon.className = "conn-modal-icon " + channelId;
    modalIcon.innerHTML = channel.icon;

    // Set title
    modalTitle.textContent = channel.name;

    // Remember which channel this modal is showing so the
    // "View Documentation" button knows which docs key to fetch.
    currentModalChannelId = channelId;

    if (channelId === "email") {
      modalBody.innerHTML = renderEmailModalBody(channelState);
    } else {
      // Generic single-provider layout for other channels
      const isConnected = channelState.status === "connected";
      const statusClass = isConnected ? "connected" : "disconnected";
      const statusText = isConnected
        ? "Channel is active and receiving messages"
        : "Channel is not configured";
      const badgeText = isConnected ? "CONNECTED" : "NOT CONFIGURED";

      modalBody.innerHTML = `
        <div class="conn-modal-status ${statusClass}">
          <div class="conn-modal-status-dot"></div>
          <span class="conn-modal-status-text">${statusText}</span>
          <span class="conn-modal-status-badge">${badgeText}</span>
        </div>
        <p class="conn-modal-info">${channel.setupInfo}</p>
        <p class="conn-modal-info">Add to your <code>symi.json</code> config:</p>
        <pre class="conn-modal-config">${escHtml(channel.configExample)}</pre>
      `;
    }

    // Show modal
    modalOverlay.classList.add("open");
    modalOverlay.setAttribute("aria-hidden", "false");
    document.addEventListener("keydown", onModalKey);
  }

  function closeModal() {
    modalOverlay.classList.remove("open");
    modalOverlay.setAttribute("aria-hidden", "true");
    document.removeEventListener("keydown", onModalKey);
  }

  function onModalKey(e) {
    if (e.key === "Escape") {
      closeModal();
    }
  }

  // Modal event listeners
  if (modalClose) {
    modalClose.addEventListener("click", closeModal);
  }
  if (modalOverlay) {
    modalOverlay.addEventListener("click", (e) => {
      if (e.target === modalOverlay) {
        closeModal();
      }
    });
  }

  // "View Documentation" button → open in-app docs viewer.
  // The docs-viewer overlay stacks on top of the conn-modal.
  if (modalDocs) {
    modalDocs.addEventListener("click", (e) => {
      e.preventDefault();
      if (currentModalChannelId) {
        void openDocsViewer(currentModalChannelId);
      }
    });
  }

  // Docs viewer event listeners
  if (docsViewerClose) {
    docsViewerClose.addEventListener("click", closeDocsViewer);
  }
  if (docsViewerOverlay) {
    docsViewerOverlay.addEventListener("click", (e) => {
      if (e.target === docsViewerOverlay) {
        closeDocsViewer();
      }
    });
  }

  // ── Handle Click ─────────────────────────────────────────────────────────
  function handleChannelClick(channelId) {
    openModal(channelId);
  }

  // ── Initialize ───────────────────────────────────────────────────────────
  function init() {
    // Bind click handlers
    Object.keys(elements).forEach((channelId) => {
      const el = elements[channelId];
      if (el?.btn) {
        el.btn.addEventListener("click", () => handleChannelClick(channelId));
      }
    });

    // Initial status check
    void checkChannelStatus();

    // Poll for status updates every 30 seconds
    setInterval(checkChannelStatus, 30000);

    // Also check when gateway connects
    window.addEventListener("gateway:connected", checkChannelStatus);
  }

  // Wait for DOM and gateway
  if (document.readyState === "loading") {
    document.addEventListener("DOMContentLoaded", init);
  } else {
    init();
  }

  // Expose for external use
  window.connectionsPanel = {
    refresh: checkChannelStatus,
    setNotifications: (channelId, count) => {
      if (state[channelId]) {
        state[channelId].notifications = count;
        updateChannelUI(channelId);
      }
    },
  };
})();
