<!doctype html>
<html lang="en">
  {% from "partials/header.html" import header with context %}
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>{{ app_name }} v{{ app_version }} - Settings</title>
    <script src="https://cdn.tailwindcss.com"></script>
    <link
      rel="stylesheet"
      href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css"
    />
    <!-- Favicon -->
    <link rel="icon" href="/static/FenLiu.png" type="image/png" />
    <link rel="apple-touch-icon" href="/static/FenLiu.png" />
    <style></style>
  </head>
  <body class="bg-gray-50 min-h-screen">
    {{ header('settings') }}

    <main class="container mx-auto px-4 py-8 max-w-5xl">
      <div class="mb-8">
        <h2 class="text-3xl font-bold text-gray-800">
          <i class="fas fa-cog mr-3 text-indigo-600"></i>Settings
        </h2>
        <p class="text-gray-600 mt-2">
          Fine-grained export gate filters applied after the review/approval
          step. Only posts that pass all active filters are eligible for Curated
          export.
        </p>
      </div>

      <!-- Flash message area -->
      <div id="flash" class="hidden mb-4"></div>

      <!-- ================================================================
           SECTION 1 – Export Settings Toggles
           ================================================================ -->
      <section class="bg-white rounded-lg shadow-md p-6 mb-8">
        <h3 class="text-xl font-bold text-gray-800 mb-6">
          <i class="fas fa-sliders-h mr-2 text-indigo-500"></i>Export Settings
        </h3>
        <form method="POST" action="/settings" class="space-y-6">
          <!-- Hidden fields for unchecked checkboxes -->
          <input type="hidden" name="attachments_only" value="off" />
          <input type="hidden" name="auto_reject_on_fetch" value="off" />
          <input type="hidden" name="auto_reject_bots" value="off" />

          <!-- Attachments-Only Toggle -->
          <div class="flex items-start justify-between">
            <div class="flex-1 mr-6">
              <h4 class="text-base font-semibold text-gray-800 mb-1">
                <i class="fas fa-image mr-2 text-blue-500"></i>Attachments-Only
                Export
              </h4>
              <p class="text-gray-500 text-sm">
                When enabled, only posts that contain at least one media
                attachment are eligible for Curated export. Useful for accounts
                that exclusively reblog visual content.
              </p>
            </div>
            <div class="flex items-center gap-3 flex-shrink-0">
              <input
                type="checkbox"
                name="attachments_only"
                value="on"
                class="w-5 h-5 cursor-pointer accent-indigo-600"
                {%
                if
                attachments_only
                %}checked{%
                endif
                %}
              />
              <span class="text-sm font-medium text-gray-700 min-w-10">
                {% if attachments_only %}On{% else %}Off{% endif %}
              </span>
            </div>
          </div>

          <hr class="border-gray-100" />

          <!-- Auto-Reject on Fetch Toggle -->
          <div class="flex items-start justify-between">
            <div class="flex-1 mr-6">
              <h4 class="text-base font-semibold text-gray-800 mb-1">
                <i class="fas fa-robot mr-2 text-amber-500"></i>Auto-Reject
                Blocked Posts on Fetch
              </h4>
              <p class="text-gray-500 text-sm">
                When enabled, any post matching the blocked-user list,
                blocked-hashtag list, or attachments-only rule is automatically
                marked as rejected immediately after it is fetched — it will
                never appear in the review queue. A system note is recorded
                explaining which filter triggered the rejection.
              </p>
            </div>
            <div class="flex items-center gap-3 flex-shrink-0">
              <input
                type="checkbox"
                name="auto_reject_on_fetch"
                value="on"
                class="w-5 h-5 cursor-pointer accent-indigo-600"
                {%
                if
                auto_reject_on_fetch
                %}checked{%
                endif
                %}
              />
              <span class="text-sm font-medium text-gray-700 min-w-10">
                {% if auto_reject_on_fetch %}On{% else %}Off{% endif %}
              </span>
            </div>
          </div>

          <hr class="border-gray-100" />

          <!-- Auto-Reject Bot Accounts Toggle -->
          <div class="flex items-start justify-between">
            <div class="flex-1 mr-6">
              <h4 class="text-base font-semibold text-gray-800 mb-1">
                <i class="fas fa-bot mr-2 text-purple-500"></i>Auto-Reject Bot
                Accounts
              </h4>
              <p class="text-gray-500 text-sm">
                When enabled, any post from an account that identifies itself as
                a bot or automated service is automatically marked as rejected
                immediately after it is fetched. A system note is recorded
                explaining that the author is a bot account.
              </p>
            </div>
            <div class="flex items-center gap-3 flex-shrink-0">
              <input
                type="checkbox"
                name="auto_reject_bots"
                value="on"
                class="w-5 h-5 cursor-pointer accent-indigo-600"
                {%
                if
                auto_reject_bots
                %}checked{%
                endif
                %}
              />
              <span class="text-sm font-medium text-gray-700 min-w-10">
                {% if auto_reject_bots %}On{% else %}Off{% endif %}
              </span>
            </div>
          </div>

          <hr class="border-gray-100" />

          <!-- Save Button -->
          <div class="flex justify-end">
            <button
              type="submit"
              class="px-4 py-2 bg-indigo-600 text-white rounded-lg text-sm font-medium hover:bg-indigo-700 focus:outline-none focus:ring-2 focus:ring-indigo-500"
            >
              <i class="fas fa-save mr-1"></i>Save Settings
            </button>
          </div>
        </form>
      </section>

      <!-- ================================================================
           SECTION 2 – API Key Management
           ================================================================ -->
      <section class="bg-white rounded-lg shadow-md p-6 mb-8">
        <h3 class="text-xl font-bold text-gray-800 mb-4">
          <i class="fas fa-key mr-2 text-green-500"></i>API Key Management
        </h3>
        <p class="text-gray-500 text-sm mb-6">
          Generate an API key to authenticate requests to protected API
          endpoints. Each new key overwrites the previous one. Store your key
          securely — it cannot be retrieved later.
        </p>

        <!-- API Key Actions -->
        <div class="space-y-3 mb-6">
          <button
            onclick="generateNewAPIKey()"
            class="px-4 py-2 bg-green-600 text-white rounded-lg text-sm font-medium hover:bg-green-700 focus:outline-none focus:ring-2 focus:ring-green-500"
          >
            <i class="fas fa-refresh mr-1"></i>Generate New API Key
          </button>
          <button
            id="revoke-key-btn"
            onclick="revokeAPIKey()"
            class="px-4 py-2 bg-red-600 text-white rounded-lg text-sm font-medium hover:bg-red-700 focus:outline-none focus:ring-2 focus:ring-red-500 disabled:bg-gray-400 disabled:cursor-not-allowed"
            disabled
          >
            <i class="fas fa-trash mr-1"></i>Revoke API Key
          </button>
        </div>

        <!-- API Key Status Display -->
        <div class="bg-gray-50 rounded-lg p-4 mb-4 border border-gray-200">
          <p class="text-sm text-gray-600 mb-2">
            <strong>Status:</strong>
            <span id="api-key-status" class="text-gray-800">Checking...</span>
          </p>
          <p class="text-xs text-gray-500 mt-3">
            Use the API key in the
            <code class="bg-white px-2 py-1 rounded">X-API-Key</code> header for
            API requests:
          </p>
          <code
            class="block bg-white border border-gray-300 rounded px-3 py-2 mt-2 text-xs text-gray-700 overflow-x-auto"
          >
            curl -H "X-API-Key: your-api-key" https://your-instance/api/v1/posts
          </code>
        </div>

        <!-- Generated Key Display (shown only after generation) -->
        <div
          id="generated-key-container"
          class="hidden bg-amber-50 border border-amber-200 rounded-lg p-4 mb-4"
        >
          <p class="text-sm font-semibold text-amber-900 mb-2">
            <i class="fas fa-exclamation-triangle mr-1"></i>Your API Key (Save
            it now!)
          </p>
          <div
            class="bg-white border border-amber-300 rounded px-3 py-2 mb-3 flex items-center justify-between"
          >
            <code
              id="generated-key-display"
              class="text-sm text-gray-800 font-mono flex-1 break-all"
            ></code>
            <button
              onclick="copyAPIKeyToClipboard()"
              class="ml-3 px-3 py-1 bg-indigo-600 text-white rounded text-xs font-medium hover:bg-indigo-700 whitespace-nowrap"
            >
              <i class="fas fa-copy mr-1"></i>Copy
            </button>
          </div>
          <p class="text-xs text-amber-800">
            This is the only time your API key will be displayed. Store it
            securely in your application or password manager.
          </p>
        </div>
      </section>

      <!-- ================================================================
           SECTION 3 – Blocked Users
           ================================================================ -->
      <section class="bg-white rounded-lg shadow-md p-6 mb-8">
        <h3 class="text-xl font-bold text-gray-800 mb-4">
          <i class="fas fa-user-slash mr-2 text-red-500"></i>Don't Reblog —
          Users
        </h3>
        <p class="text-gray-500 text-sm mb-4">
          Posts authored by accounts matching any pattern on this list are
          automatically excluded from all Curated exports, regardless of
          approval status.
        </p>

        <!-- Pattern type hints -->
        <div
          class="bg-indigo-50 border border-indigo-200 rounded-lg p-4 mb-6 text-sm text-gray-700"
        >
          <strong class="block mb-2">Pattern types:</strong>
          <ul class="space-y-1 ml-4">
            <li><strong>exact:</strong> @user@instance.social</li>
            <li>
              <strong>suffix:</strong> ends with, e.g. *.bsky.app to block all
              Bluesky users
            </li>
            <li>
              <strong>prefix:</strong> starts with, e.g. bot* to block bot
              accounts
            </li>
            <li><strong>contains:</strong> substring match, e.g. spam</li>
          </ul>
        </div>

        <!-- Add user form -->
        <form
          id="add-user-form"
          class="flex gap-3 mb-6 flex-wrap"
          onsubmit="addBlockedUser(event)"
        >
          <input
            type="text"
            id="new-user-input"
            name="account_identifier"
            placeholder="@user@instance.social or *.bsky.app"
            class="flex-1 min-w-48 px-4 py-2 border border-gray-300 rounded-lg text-sm focus:outline-none focus:ring-2 focus:ring-indigo-500"
            required
          />
          <select
            id="new-user-pattern-type"
            name="pattern_type"
            class="px-4 py-2 border border-gray-300 rounded-lg text-sm focus:outline-none focus:ring-2 focus:ring-indigo-500 bg-white"
          >
            <option value="exact">exact</option>
            <option value="suffix">suffix</option>
            <option value="prefix">prefix</option>
            <option value="contains">contains</option>
          </select>
          <input
            type="text"
            id="new-user-notes"
            name="notes"
            placeholder="Notes (optional)"
            class="w-48 px-4 py-2 border border-gray-300 rounded-lg text-sm focus:outline-none focus:ring-2 focus:ring-indigo-500"
          />
          <button
            type="submit"
            class="px-4 py-2 bg-red-600 text-white rounded-lg text-sm font-medium hover:bg-red-700 focus:outline-none focus:ring-2 focus:ring-red-500 whitespace-nowrap"
          >
            <i class="fas fa-plus mr-1"></i>Block
          </button>
        </form>

        <!-- User blocklist table -->
        <div id="blocked-users-container">
          {% if blocked_users %}
          <table class="w-full text-sm">
            <thead>
              <tr class="text-left text-gray-500 border-b">
                <th class="pb-2 font-medium">Pattern</th>
                <th class="pb-2 font-medium">Type</th>
                <th class="pb-2 font-medium">Notes</th>
                <th class="pb-2 font-medium">Added</th>
                <th class="pb-2"></th>
              </tr>
            </thead>
            <tbody id="blocked-users-tbody">
              {% for user in blocked_users %}
              <tr
                class="border-b border-gray-100 hover:bg-gray-50"
                id="user-row-{{ user.id }}"
              >
                <td class="py-3 font-mono text-gray-800">
                  {{ user.account_identifier }}
                </td>
                <td class="py-3 text-sm">
                  <span
                    class="inline-block px-2 py-1 rounded bg-gray-100 text-gray-700"
                  >
                    {{ user.pattern_type }}
                  </span>
                </td>
                <td class="py-3 text-gray-500">{{ user.notes or '—' }}</td>
                <td class="py-3 text-gray-400">
                  {{ user.created_at.strftime('%Y-%m-%d') if user.created_at
                  else '—' }}
                </td>
                <td class="py-3 text-right">
                  <button
                    onclick="deleteBlockedUser({{ user.id }})"
                    class="text-red-400 hover:text-red-600 transition-colors"
                    title="Remove from blocklist"
                  >
                    <i class="fas fa-trash-alt"></i>
                  </button>
                </td>
              </tr>
              {% endfor %}
            </tbody>
          </table>
          {% else %}
          <p id="blocked-users-empty" class="text-gray-400 text-sm italic">
            No users blocked yet.
          </p>
          {% endif %}
        </div>
      </section>

      <!-- ================================================================
           SECTION 4 – Blocked Hashtags
           ================================================================ -->
      <section class="bg-white rounded-lg shadow-md p-6 mb-8">
        <h3 class="text-xl font-bold text-gray-800 mb-4">
          <i class="fas fa-hashtag mr-2 text-orange-500"></i>Don't Reblog —
          Hashtags
        </h3>
        <p class="text-gray-500 text-sm mb-6">
          Any post whose content contains one or more of the hashtags below is
          automatically excluded from export. The filter operates on post
          content — not on stream hashtags — so a post sourced from a monitored
          stream can still be blocked if its body carries a blocked tag.
        </p>

        <!-- Add hashtag form -->
        <form
          id="add-hashtag-form"
          class="flex gap-3 mb-6"
          onsubmit="addBlockedHashtag(event)"
        >
          <input
            type="text"
            id="new-hashtag-input"
            name="hashtag"
            placeholder="spam (or #spam)"
            class="flex-1 px-4 py-2 border border-gray-300 rounded-lg text-sm focus:outline-none focus:ring-2 focus:ring-indigo-500"
            required
          />
          <input
            type="text"
            id="new-hashtag-notes"
            name="notes"
            placeholder="Notes (optional)"
            class="w-48 px-4 py-2 border border-gray-300 rounded-lg text-sm focus:outline-none focus:ring-2 focus:ring-indigo-500"
          />
          <button
            type="submit"
            class="px-4 py-2 bg-orange-600 text-white rounded-lg text-sm font-medium hover:bg-orange-700 focus:outline-none focus:ring-2 focus:ring-orange-500 whitespace-nowrap"
          >
            <i class="fas fa-plus mr-1"></i>Block Hashtag
          </button>
        </form>

        <!-- Hashtag blocklist table -->
        <div id="blocked-hashtags-container">
          {% if blocked_hashtags %}
          <table class="w-full text-sm">
            <thead>
              <tr class="text-left text-gray-500 border-b">
                <th class="pb-2 font-medium">Hashtag</th>
                <th class="pb-2 font-medium">Notes</th>
                <th class="pb-2 font-medium">Added</th>
                <th class="pb-2"></th>
              </tr>
            </thead>
            <tbody id="blocked-hashtags-tbody">
              {% for tag in blocked_hashtags %}
              <tr
                class="border-b border-gray-100 hover:bg-gray-50"
                id="hashtag-row-{{ tag.id }}"
              >
                <td class="py-3 font-mono text-gray-800">#{{ tag.hashtag }}</td>
                <td class="py-3 text-gray-500">{{ tag.notes or '—' }}</td>
                <td class="py-3 text-gray-400">
                  {{ tag.created_at.strftime('%Y-%m-%d') if tag.created_at else
                  '—' }}
                </td>
                <td class="py-3 text-right">
                  <button
                    onclick="deleteBlockedHashtag({{ tag.id }})"
                    class="text-red-400 hover:text-red-600 transition-colors"
                    title="Remove from blocklist"
                  >
                    <i class="fas fa-trash-alt"></i>
                  </button>
                </td>
              </tr>
              {% endfor %}
            </tbody>
          </table>
          {% else %}
          <p id="blocked-hashtags-empty" class="text-gray-400 text-sm italic">
            No hashtags blocked yet.
          </p>
          {% endif %}
        </div>
      </section>
    </main>

    <script>
      /* ================================================================
       * API Key and Blocklist Management (still needs JavaScript for forms)
       * ================================================================ */
      // Store API key from server context
      let apiKey = "{{ api_key }}";

      // Helper function to add API key to fetch headers
      function getHeaders(method = "GET", additionalHeaders = {}) {
        const headers = {
          "Content-Type": "application/json",
          ...additionalHeaders,
        };
        if (apiKey) {
          headers["X-API-Key"] = apiKey;
        }
        return headers;
      }

      /* ------------------------------------------------------------------ *
       * Utility helpers                                                       *
       * ------------------------------------------------------------------ */
      function showFlash(message, type) {
        const el = document.getElementById("flash");
        const colours =
          type === "error"
            ? "bg-red-100 border border-red-400 text-red-700"
            : "bg-green-100 border border-green-400 text-green-700";
        el.className = `${colours} px-4 py-3 rounded mb-4`;
        el.textContent = message;
        el.classList.remove("hidden");
        setTimeout(() => el.classList.add("hidden"), 4000);
      }

      function formatDate(isoString) {
        if (!isoString) return "—";
        return isoString.slice(0, 10);
      }
      async function checkAPIKeyStatus() {
        try {
          const resp = await fetch("/api/v1/api-keys/status");
          if (!resp.ok) throw new Error("Failed to check API key status");
          const data = await resp.json();
          const statusEl = document.getElementById("api-key-status");
          const revokeBtn = document.getElementById("revoke-key-btn");
          if (data.api_key_set) {
            statusEl.textContent = "API key is set and active";
            statusEl.className = "text-green-600 font-semibold";
            revokeBtn.disabled = false;
          } else {
            statusEl.textContent = "No API key configured";
            statusEl.className = "text-gray-600";
            revokeBtn.disabled = true;
          }
        } catch (e) {
          showFlash("Failed to check API key status: " + e.message, "error");
        }
      }

      async function generateNewAPIKey() {
        try {
          const resp = await fetch("/api/v1/api-keys/generate", {
            method: "POST",
          });
          if (!resp.ok) {
            const err = await resp.json();
            throw new Error(err.error || "Failed to generate API key");
          }

          const data = await resp.json();
          const container = document.getElementById("generated-key-container");
          const display = document.getElementById("generated-key-display");

          display.textContent = data.api_key;
          container.classList.remove("hidden");

          // Store API key in localStorage for use by queue page
          localStorage.setItem("fenliu_api_key", data.api_key);

          showFlash(data.message, "success");
          await checkAPIKeyStatus();
        } catch (e) {
          showFlash("Failed to generate API key: " + e.message, "error");
        }
      }

      async function revokeAPIKey() {
        if (
          !confirm("Revoke the current API key? This action cannot be undone.")
        ) {
          return;
        }

        try {
          const resp = await fetch("/api/v1/api-keys/revoke", {
            method: "POST",
          });
          if (!resp.ok) {
            const err = await resp.json();
            throw new Error(err.error || "Failed to revoke API key");
          }

          document
            .getElementById("generated-key-container")
            .classList.add("hidden");

          // Remove API key from localStorage when revoked
          localStorage.removeItem("fenliu_api_key");

          showFlash("API key revoked successfully", "success");
          await checkAPIKeyStatus();
        } catch (e) {
          showFlash("Failed to revoke API key: " + e.message, "error");
        }
      }

      function copyAPIKeyToClipboard() {
        const keyText = document.getElementById(
          "generated-key-display",
        ).textContent;

        // Store API key in localStorage when copied
        localStorage.setItem("fenliu_api_key", keyText);

        navigator.clipboard
          .writeText(keyText)
          .then(() => {
            showFlash("API key copied to clipboard", "success");
          })
          .catch(() => {
            showFlash("Failed to copy API key", "error");
          });
      }

      // Check API key status on page load
      document.addEventListener("DOMContentLoaded", () => {
        checkAPIKeyStatus();
      });

      /* ------------------------------------------------------------------ *
       * Blocked Users                                                         *
       * ------------------------------------------------------------------ */
      async function addBlockedUser(event) {
        event.preventDefault();
        const identifier = document
          .getElementById("new-user-input")
          .value.trim();
        const patternType = document
          .getElementById("new-user-pattern-type")
          .value.trim();
        const notes =
          document.getElementById("new-user-notes").value.trim() || null;
        if (!identifier) return;

        try {
          const resp = await fetch("/api/v1/reblog-controls/blocked-users", {
            method: "POST",
            headers: getHeaders("POST"),
            body: JSON.stringify({
              account_identifier: identifier,
              pattern_type: patternType,
              notes,
            }),
          });
          if (!resp.ok) {
            const err = await resp.json();
            showFlash(
              "Error: " + (err.detail || err.error || resp.status),
              "error",
            );
            return;
          }
          const user = await resp.json();
          appendBlockedUserRow(user);
          document.getElementById("new-user-input").value = "";
          document.getElementById("new-user-pattern-type").value = "exact";
          document.getElementById("new-user-notes").value = "";
          showFlash("Pattern blocked: " + user.account_identifier, "success");
        } catch (e) {
          showFlash("Network error: " + e.message, "error");
        }
      }

      function appendBlockedUserRow(user) {
        // Remove empty-state paragraph if present
        const empty = document.getElementById("blocked-users-empty");
        if (empty) empty.remove();

        let tbody = document.getElementById("blocked-users-tbody");
        if (!tbody) {
          // First entry — build the table skeleton
          const container = document.getElementById("blocked-users-container");
          container.innerHTML = `
            <table class="w-full text-sm">
              <thead>
                <tr class="text-left text-gray-500 border-b">
                  <th class="pb-2 font-medium">Pattern</th>
                  <th class="pb-2 font-medium">Type</th>
                  <th class="pb-2 font-medium">Notes</th>
                  <th class="pb-2 font-medium">Added</th>
                  <th class="pb-2"></th>
                </tr>
              </thead>
              <tbody id="blocked-users-tbody"></tbody>
            </table>`;
          tbody = document.getElementById("blocked-users-tbody");
        }

        const tr = document.createElement("tr");
        tr.id = `user-row-${user.id}`;
        tr.className = "border-b border-gray-100 hover:bg-gray-50";
        tr.innerHTML = `
          <td class="py-3 font-mono text-gray-800">${escapeHtml(user.account_identifier)}</td>
          <td class="py-3 text-sm"><span class="inline-block px-2 py-1 rounded bg-gray-100 text-gray-700">${escapeHtml(user.pattern_type)}</span></td>
          <td class="py-3 text-gray-500">${escapeHtml(user.notes || "—")}</td>
          <td class="py-3 text-gray-400">${formatDate(user.created_at)}</td>
          <td class="py-3 text-right">
            <button onclick="deleteBlockedUser(${user.id})"
              class="text-red-400 hover:text-red-600 transition-colors"
              title="Remove from blocklist">
              <i class="fas fa-trash-alt"></i>
            </button>
          </td>`;
        tbody.prepend(tr);
      }

      async function deleteBlockedUser(userId) {
        try {
          const resp = await fetch(
            `/api/v1/reblog-controls/blocked-users/${userId}`,
            { method: "DELETE", headers: getHeaders("DELETE") },
          );
          if (resp.status === 204) {
            const row = document.getElementById(`user-row-${userId}`);
            if (row) row.remove();
            const tbody = document.getElementById("blocked-users-tbody");
            if (tbody && tbody.children.length === 0) {
              document.getElementById("blocked-users-container").innerHTML =
                '<p id="blocked-users-empty" class="text-gray-400 text-sm italic">No users blocked yet.</p>';
            }
            showFlash("Pattern removed from blocklist.", "success");
          } else {
            const err = await resp.json();
            showFlash(
              "Error: " + (err.detail || err.error || resp.status),
              "error",
            );
          }
        } catch (e) {
          showFlash("Network error: " + e.message, "error");
        }
      }

      /* ------------------------------------------------------------------ *
       * Blocked Hashtags                                                      *
       * ------------------------------------------------------------------ */
      async function addBlockedHashtag(event) {
        event.preventDefault();
        const hashtag = document
          .getElementById("new-hashtag-input")
          .value.trim();
        const notes =
          document.getElementById("new-hashtag-notes").value.trim() || null;
        if (!hashtag) return;

        try {
          const resp = await fetch("/api/v1/reblog-controls/blocked-hashtags", {
            method: "POST",
            headers: getHeaders("POST"),
            body: JSON.stringify({ hashtag, notes }),
          });
          if (!resp.ok) {
            const err = await resp.json();
            showFlash(
              "Error: " + (err.detail || err.error || resp.status),
              "error",
            );
            return;
          }
          const tag = await resp.json();
          appendBlockedHashtagRow(tag);
          document.getElementById("new-hashtag-input").value = "";
          document.getElementById("new-hashtag-notes").value = "";
          showFlash("Hashtag blocked: #" + tag.hashtag, "success");
        } catch (e) {
          showFlash("Network error: " + e.message, "error");
        }
      }

      function appendBlockedHashtagRow(tag) {
        const empty = document.getElementById("blocked-hashtags-empty");
        if (empty) empty.remove();

        let tbody = document.getElementById("blocked-hashtags-tbody");
        if (!tbody) {
          const container = document.getElementById(
            "blocked-hashtags-container",
          );
          container.innerHTML = `
            <table class="w-full text-sm">
              <thead>
                <tr class="text-left text-gray-500 border-b">
                  <th class="pb-2 font-medium">Hashtag</th>
                  <th class="pb-2 font-medium">Notes</th>
                  <th class="pb-2 font-medium">Added</th>
                  <th class="pb-2"></th>
                </tr>
              </thead>
              <tbody id="blocked-hashtags-tbody"></tbody>
            </table>`;
          tbody = document.getElementById("blocked-hashtags-tbody");
        }

        const tr = document.createElement("tr");
        tr.id = `hashtag-row-${tag.id}`;
        tr.className = "border-b border-gray-100 hover:bg-gray-50";
        tr.innerHTML = `
          <td class="py-3 font-mono text-gray-800">#${escapeHtml(tag.hashtag)}</td>
          <td class="py-3 text-gray-500">${escapeHtml(tag.notes || "—")}</td>
          <td class="py-3 text-gray-400">${formatDate(tag.created_at)}</td>
          <td class="py-3 text-right">
            <button onclick="deleteBlockedHashtag(${tag.id})"
              class="text-red-400 hover:text-red-600 transition-colors"
              title="Remove from blocklist">
              <i class="fas fa-trash-alt"></i>
            </button>
          </td>`;
        tbody.prepend(tr);
      }

      async function deleteBlockedHashtag(hashtagId) {
        try {
          const resp = await fetch(
            `/api/v1/reblog-controls/blocked-hashtags/${hashtagId}`,
            { method: "DELETE", headers: getHeaders("DELETE") },
          );
          if (resp.status === 204) {
            const row = document.getElementById(`hashtag-row-${hashtagId}`);
            if (row) row.remove();
            const tbody = document.getElementById("blocked-hashtags-tbody");
            if (tbody && tbody.children.length === 0) {
              document.getElementById("blocked-hashtags-container").innerHTML =
                '<p id="blocked-hashtags-empty" class="text-gray-400 text-sm italic">No hashtags blocked yet.</p>';
            }
            showFlash("Hashtag removed from blocklist.", "success");
          } else {
            const err = await resp.json();
            showFlash(
              "Error: " + (err.detail || err.error || resp.status),
              "error",
            );
          }
        } catch (e) {
          showFlash("Network error: " + e.message, "error");
        }
      }

      /* ------------------------------------------------------------------ *
       * XSS helper                                                            *
       * ------------------------------------------------------------------ */
      function escapeHtml(str) {
        return String(str)
          .replace(/&/g, "&amp;")
          .replace(/</g, "&lt;")
          .replace(/>/g, "&gt;")
          .replace(/"/g, "&quot;")
          .replace(/'/g, "&#39;");
      }
    </script>
  </body>
</html>
