import { Context, Hono } from "hono";
import { deleteCookie, getSignedCookie, setSignedCookie } from "hono/cookie";
import { zValidator } from "@hono/zod-validator";
import { z } from "zod";
import { Env } from "../types";
import { csrf } from "hono/csrf";
import { ADMIN_COOKIE_MAX_AGE } from "../config/constants";
import { logger } from "../lib/logger";
import { timingSafeEqual, checkProxyAuth } from "../lib/auth";
import { Layout, clampText } from "./admin/ui";
import { FeedRepository } from "../domain/feed-repository";
import { renameFeed } from "../lib/feed-service";
import { feedRssUrl, feedAtomUrl, feedEmailAddress } from "../utils/urls";
import { feedsRouter } from "./admin/feeds";
import { emailsRouter } from "./admin/emails";
import { dashboardScript } from "../scripts/generated/dashboard";

type AppEnv = { Bindings: Env };

/**
 * Admin routes handler for Email-to-RSS
 * Provides a secure interface for managing RSS feeds and viewing emails
 *
 * Security:
 * - All routes except /login are protected by server-side cookie authentication
 * - Uses HttpOnly cookies to prevent XSS attacks
 * - Implements SameSite=Strict to prevent CSRF attacks
 */
const app = new Hono<AppEnv>();

// Export for testing
export default app;

const ADMIN_COOKIE_NAME = "admin_auth";

// Prevent accidental caching of admin pages and redirects.
app.use("*", async (c, next) => {
  c.header("Cache-Control", "no-store, max-age=0");
  await next();
});

// Authentication middleware for admin routes
async function authMiddleware(c: Context, next: () => Promise<void>) {
  const env = c.env;
  const path = new URL(c.req.url).pathname;

  // Skip auth check for login page - note that path includes /admin prefix
  if (path === "/admin/login") {
    return next();
  }

  // Proxy auth: only active when both env vars are present
  if (checkProxyAuth(c, env)) {
    return next();
  }

  // Fallback: signed cookie
  const authCookie = await getSignedCookie(
    c,
    env.ADMIN_PASSWORD,
    ADMIN_COOKIE_NAME,
  );
  if (authCookie !== "1") {
    return c.redirect("/admin/login");
  }

  await next();
}

// Apply auth middleware to all admin routes
app.use("*", authMiddleware);

// CSRF middleware: validates Origin header on mutating requests (POST/PUT/DELETE/PATCH)
// Skip on /admin/login — password itself provides protection for the pre-auth route
const csrfMiddleware = csrf({
  origin: (origin, c) => origin === `https://${c.env.DOMAIN}`,
});
app.use("*", (c, next) => {
  const path = new URL(c.req.url).pathname;
  if (path === "/admin/login") return next();
  return csrfMiddleware(c, next);
});

// Schema for feed API updates (title/description only)
const updateFeedSchema = z.object({
  title: z.string().min(1, "Title is required"),
  description: z.string().optional(),
  language: z.string().optional().default("en"),
  allowedSenders: z.array(z.string()).optional().default([]),
});

// Authentication schema
const authSchema = z.object({
  password: z.string().min(1, "Password is required"),
});

// Login page
app.get("/login", (c) => {
  const error = c.req.query("error");
  const errorMessage =
    error === "invalid" ? "Invalid password. Please try again." : "";

  return c.html(
    <Layout title="Login">
      <div class="auth-container fade-in">
        <div class="auth-card">
          <div class="auth-logo">
            <svg
              width="64"
              height="64"
              viewBox="0 0 24 24"
              fill="none"
              xmlns="http://www.w3.org/2000/svg"
            >
              <rect
                width="24"
                height="24"
                rx="12"
                fill="var(--color-primary)"
              />
              <path
                d="M17 9C17 7.89543 16.1046 7 15 7H9C7.89543 7 7 7.89543 7 9V15C7 16.1046 7.89543 17 9 17H15C16.1046 17 17 16.1046 17 15V9Z"
                stroke="white"
                stroke-width="1.5"
              />
              <path
                d="M7 9L12 13L17 9"
                stroke="white"
                stroke-width="1.5"
                stroke-linecap="round"
                stroke-linejoin="round"
              />
            </svg>
          </div>
          <h1 class="auth-title">kill-the-news</h1>
          {errorMessage && <div class="auth-error">{errorMessage}</div>}
          <form class="auth-form" action="/admin/login" method="post">
            <div class="form-group">
              <label for="password">Password</label>
              <input
                type="password"
                id="password"
                name="password"
                required
                autofocus
              />
            </div>
            <button type="submit" class="button auth-button">
              Log In
            </button>
          </form>
        </div>
      </div>
    </Layout>,
  );
});

// Handle login
app.post("/login", async (c) => {
  const env = c.env;

  try {
    const formData = await c.req.formData();
    const password = formData.get("password")?.toString() || "";

    // Validate password
    authSchema.parse({ password });

    // Check password against environment variable
    if (timingSafeEqual(password, env.ADMIN_PASSWORD)) {
      await setSignedCookie(c, ADMIN_COOKIE_NAME, "1", env.ADMIN_PASSWORD, {
        path: "/",
        httpOnly: true,
        sameSite: "Strict",
        secure: true,
        maxAge: ADMIN_COOKIE_MAX_AGE,
      });
      return c.redirect("/admin");
    }

    // Incorrect password - redirect back to login with an error message
    return c.redirect("/admin/login?error=invalid");
  } catch (error) {
    logger.error("Login error", { error: String(error) });
    return c.redirect("/admin/login?error=invalid");
  }
});

// Logout route
app.get("/logout", (c) => {
  deleteCookie(c, ADMIN_COOKIE_NAME, { path: "/" });
  return c.redirect("/admin/login");
});

// dashboardScript is compiled from src/scripts/client/dashboard.ts via `npm run build:client`.
// It is imported from src/scripts/generated/dashboard.ts above.

// ── Shared SVG icons ──────────────────────────────────────────────────────────

const CopyIcon = () => (
  <svg
    class="copy-icon copy-icon-original"
    width="16"
    height="16"
    viewBox="0 0 24 24"
    fill="none"
    stroke="currentColor"
    stroke-width="2"
    stroke-linecap="round"
    stroke-linejoin="round"
  >
    <rect x="9" y="9" width="13" height="13" rx="2" ry="2"></rect>
    <path d="M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1"></path>
  </svg>
);

const CheckIcon = () => (
  <svg
    class="copy-icon copy-icon-success"
    width="16"
    height="16"
    viewBox="0 0 24 24"
    fill="none"
    stroke="currentColor"
    stroke-width="2"
    stroke-linecap="round"
    stroke-linejoin="round"
  >
    <path d="M20 6L9 17l-5-5"></path>
  </svg>
);

type CopyFieldInlineProps = {
  value: string;
  emailAddress?: string;
};

const CopyFieldInline = ({ value }: CopyFieldInlineProps) => (
  <div class="copyable copyable-inline">
    <div class="copyable-content">
      <span class="copyable-value" data-copy={value} title={value}>
        {value}
      </span>
      <div class="copy-icon-container">
        <CopyIcon />
        <CheckIcon />
      </div>
    </div>
  </div>
);

function formatExpiry(expiresAt: number): { label: string; expired: boolean } {
  const remaining = expiresAt - Date.now();
  if (remaining <= 0) {
    const h = Math.floor(-remaining / 3_600_000);
    return {
      label: h > 0 ? `Expired ${h}h ago` : "Just expired",
      expired: true,
    };
  }
  const h = Math.floor(remaining / 3_600_000);
  if (h >= 48) {
    return { label: `Expires in ${Math.floor(h / 24)}d`, expired: false };
  }
  const m = Math.floor((remaining % 3_600_000) / 60_000);
  return {
    label: h > 0 ? `Expires in ${h}h ${m}m` : `Expires in ${m}m`,
    expired: false,
  };
}

const ExpiryBadge = ({ expiresAt }: { expiresAt: number }) => {
  const { label, expired } = formatExpiry(expiresAt);
  return (
    <span class={`pill ${expired ? "pill-expired" : "pill-expiry"}`}>
      {label}
    </span>
  );
};

// Admin dashboard route
app.get("/", async (c) => {
  // Type assertion for environment variables
  const env = c.env;
  const url = new URL(c.req.url);
  const view = url.searchParams.get("view") === "table" ? "table" : "list";
  const message = url.searchParams.get("message");
  const count = Number(url.searchParams.get("count") || "0");

  // List all feeds
  const feedList = await FeedRepository.from(env).listFeeds();

  // Keep the dashboard fast: avoid N KV reads for N feeds.
  // We store title/description in `feeds:list` (description is optional for older data).
  const feedsWithConfig = feedList.map((feed) => ({
    ...feed,
    description: feed.description || "",
  }));

  const listHref = (() => {
    const nextUrl = new URL(url);
    nextUrl.pathname = "/admin";
    nextUrl.searchParams.set("view", "list");
    const qs = nextUrl.searchParams.toString();
    return `${nextUrl.pathname}${qs ? `?${qs}` : ""}`;
  })();

  const tableHref = (() => {
    const nextUrl = new URL(url);
    nextUrl.pathname = "/admin";
    nextUrl.searchParams.set("view", "table");
    const qs = nextUrl.searchParams.toString();
    return `${nextUrl.pathname}${qs ? `?${qs}` : ""}`;
  })();

  const viewToggle = (
    <div class="segmented" role="tablist" aria-label="Feed view">
      <a
        class={`segmented-item ${view === "list" ? "is-active" : ""}`}
        href={listHref}
        role="tab"
        aria-selected={view === "list" ? "true" : "false"}
      >
        List
      </a>
      <a
        class={`segmented-item ${view === "table" ? "is-active" : ""}`}
        href={tableHref}
        role="tab"
        aria-selected={view === "table" ? "true" : "false"}
      >
        Table
      </a>
    </div>
  );

  return c.html(
    <Layout title="Dashboard">
      <div class="container fade-in">
        <div class="header-with-actions">
          <div class="header-title">
            <h1>kill-the-news</h1>
            <p>Manage your email newsletter feeds</p>
          </div>
          <div class="header-actions">
            <a href="/" class="button button-secondary">
              Status
            </a>
            <a href="/admin/logout" class="button button-logout">
              Logout
            </a>
          </div>
        </div>

        <details
          class="card create-feed-card"
          open={feedsWithConfig.length === 0}
        >
          <summary class="create-feed-summary">
            <h2>Create New Feed</h2>
          </summary>
          <form action="/admin/feeds/create" method="post">
            <div class="form-group">
              <label for="title">Feed Title</label>
              <input type="text" id="title" name="title" required />
            </div>

            <div class="form-group">
              <label for="description">Description</label>
              <textarea id="description" name="description" rows={3}></textarea>
            </div>

            <div class="form-group">
              <label for="allowed_senders">
                Allowed senders (optional, one email or domain per line)
              </label>
              <textarea
                id="allowed_senders"
                name="allowed_senders"
                rows={3}
                placeholder={"newsletter@example.com\ntechmeme.com"}
              ></textarea>
              <small>
                When set, inbound emails are only accepted from these
                senders/domains.
              </small>
            </div>

            <div class="form-group">
              <label for="blocked_senders">
                Blocked senders (optional, one email or domain per line)
              </label>
              <textarea
                id="blocked_senders"
                name="blocked_senders"
                rows={3}
                placeholder={"spam@example.com\nunwanted.com"}
              ></textarea>
              <small>
                Emails from these senders/domains are always rejected, even if
                they match the allowlist.
              </small>
            </div>

            <div class="form-group">
              <label for="lifetime_hours">
                Lifetime (hours{env.FEED_TTL_HOURS ? "" : ", optional"})
              </label>
              <input
                type="number"
                id="lifetime_hours"
                name="lifetime_hours"
                min="1"
                value={env.FEED_TTL_HOURS || ""}
                disabled={!!env.FEED_TTL_HOURS}
                placeholder={env.FEED_TTL_HOURS ? undefined : "No expiry"}
              />
              {env.FEED_TTL_HOURS ? (
                <small>
                  Feed lifetime is fixed to {env.FEED_TTL_HOURS}h by server
                  configuration.
                </small>
              ) : (
                <small>Leave empty for no expiry.</small>
              )}
            </div>

            <input type="hidden" id="language" name="language" value="en" />
            <input type="hidden" name="view" value={view} />

            <button type="submit" class="button">
              Create Feed
            </button>
          </form>
        </details>

        {message === "bulkDeleted" && (
          <div class="card">
            <p>Deleted {Number.isFinite(count) ? count : 0} feed(s).</p>
          </div>
        )}
        {message === "bulkDeleteNoop" && (
          <div class="card">
            <p>No feeds were selected.</p>
          </div>
        )}

        <div class="toolbar" id="your-feeds">
          <div class="toolbar-group">
            <h2 style="margin: 0;">Your Feeds</h2>
            <span class="pill" id="feed-total-count">
              {feedsWithConfig.length}
            </span>
          </div>
          <div class="toolbar-group">{viewToggle}</div>
        </div>

        {feedsWithConfig.length === 0 ? (
          <div class="card">
            <p>You don't have any feeds yet. Create one above.</p>
          </div>
        ) : view === "table" ? (
          <div class="card">
            <form
              id="bulk-feed-delete-form"
              action="/admin/feeds/bulk-delete"
              method="post"
              onsubmit="return onBulkFeedDeleteSubmit(event)"
            >
              <input type="hidden" name="view" value="table" />

              <div class="toolbar">
                <div class="toolbar-group toolbar-group-fill">
                  <input
                    type="search"
                    id="feed-search"
                    class="search"
                    placeholder="Search title, feed id, or description"
                    oninput="scheduleFeedFilter()"
                  />
                  <span class="pill" id="feed-match-count">
                    Showing {feedsWithConfig.length}
                  </span>
                  <span class="pill" id="selected-feed-count">
                    0 selected
                  </span>
                  <button
                    type="button"
                    class="button button-small button-secondary"
                    onclick="selectMatchingFeeds()"
                  >
                    Select Results
                  </button>
                  <button
                    type="button"
                    class="button button-small button-secondary"
                    onclick="clearFeedSelection()"
                  >
                    Clear Selection
                  </button>
                  <button
                    id="bulk-delete-feeds-button"
                    type="submit"
                    class="button button-small button-danger"
                    disabled
                  >
                    Delete Selected
                  </button>
                </div>
              </div>

              <div class="table-wrap">
                <table class="table table-feeds">
                  <colgroup>
                    <col data-col="select" style="width: 44px;" />
                    <col data-col="title" style="width: 280px;" />
                    <col data-col="feedId" style="width: 150px;" />
                    <col data-col="email" style="width: 200px;" />
                    <col data-col="rss" style="width: 190px;" />
                    <col data-col="atom" style="width: 190px;" />
                    <col data-col="expires" style="width: 130px;" />
                    <col data-col="actions" style="width: 170px;" />
                  </colgroup>
                  <thead>
                    <tr>
                      <th>
                        <input
                          type="checkbox"
                          id="select-all-feeds"
                          onchange="toggleAllFeeds(this.checked)"
                        />
                      </th>
                      <th
                        class="th-resizable"
                        data-sort-key="title"
                        aria-sort="none"
                      >
                        <button
                          type="button"
                          class="th-button"
                          data-sort-key="title"
                        >
                          Title
                          <span
                            class="sort-indicator"
                            aria-hidden="true"
                          ></span>
                        </button>
                        <div
                          class="col-resizer"
                          data-col="title"
                          title="Resize"
                        ></div>
                      </th>
                      <th
                        class="th-resizable"
                        data-sort-key="feedId"
                        aria-sort="none"
                      >
                        <button
                          type="button"
                          class="th-button"
                          data-sort-key="feedId"
                        >
                          Feed ID
                          <span
                            class="sort-indicator"
                            aria-hidden="true"
                          ></span>
                        </button>
                        <div
                          class="col-resizer"
                          data-col="feedId"
                          title="Resize"
                        ></div>
                      </th>
                      <th
                        class="th-resizable"
                        data-sort-key="email"
                        aria-sort="none"
                      >
                        <button
                          type="button"
                          class="th-button"
                          data-sort-key="email"
                        >
                          Email
                          <span
                            class="sort-indicator"
                            aria-hidden="true"
                          ></span>
                        </button>
                        <div
                          class="col-resizer"
                          data-col="email"
                          title="Resize"
                        ></div>
                      </th>
                      <th
                        class="th-resizable"
                        data-sort-key="rss"
                        aria-sort="none"
                      >
                        <button
                          type="button"
                          class="th-button"
                          data-sort-key="rss"
                        >
                          RSS
                          <span
                            class="sort-indicator"
                            aria-hidden="true"
                          ></span>
                        </button>
                        <div
                          class="col-resizer"
                          data-col="rss"
                          title="Resize"
                        ></div>
                      </th>
                      <th
                        class="th-resizable"
                        data-sort-key="atom"
                        aria-sort="none"
                      >
                        <button
                          type="button"
                          class="th-button"
                          data-sort-key="atom"
                        >
                          Atom
                          <span
                            class="sort-indicator"
                            aria-hidden="true"
                          ></span>
                        </button>
                        <div
                          class="col-resizer"
                          data-col="atom"
                          title="Resize"
                        ></div>
                      </th>
                      <th class="th-resizable">
                        <span>Expires</span>
                        <div
                          class="col-resizer"
                          data-col="expires"
                          title="Resize"
                        ></div>
                      </th>
                      <th class="th-resizable">
                        <span>Actions</span>
                        <div
                          class="col-resizer"
                          data-col="actions"
                          title="Resize"
                        ></div>
                      </th>
                    </tr>
                  </thead>
                  <tbody id="feed-table-body">
                    {feedsWithConfig.map((feed) => {
                      const emailAddress = feedEmailAddress(feed.id, env);
                      const rssUrl = feedRssUrl(feed.id, env);
                      const atomUrl = feedAtomUrl(feed.id, env);
                      const titleDisplay = clampText(feed.title, 160);
                      const titleHover = clampText(feed.title, 1000);
                      const sortTitle = titleHover.toLowerCase();
                      const sortFeedId = feed.id.toLowerCase();
                      const sortEmail = emailAddress.toLowerCase();
                      const sortRss = rssUrl.toLowerCase();
                      const sortAtom = atomUrl.toLowerCase();
                      const descDisplay = clampText(
                        feed.description || "",
                        220,
                      );
                      const descHover = clampText(feed.description || "", 1000);
                      const searchHaystack =
                        `${clampText(feed.title, 320)} ${feed.id} ${clampText(feed.description || "", 320)}`.toLowerCase();
                      const isExpired =
                        feed.expires_at !== undefined &&
                        feed.expires_at <= Date.now();

                      return (
                        <tr
                          class={`feed-row${isExpired ? " feed-expired" : ""}`}
                          data-feed-id={feed.id}
                          data-search={searchHaystack}
                          data-sort-title={sortTitle}
                          data-sort-feed-id={sortFeedId}
                          data-sort-email={sortEmail}
                          data-sort-rss={sortRss}
                          data-sort-atom={sortAtom}
                        >
                          <td>
                            <input
                              type="checkbox"
                              class="feed-select"
                              name="feedIds"
                              value={feed.id}
                              onchange="updateFeedSelectionState()"
                            />
                          </td>
                          <td>
                            <div class="feed-title-cell">
                              <img
                                class="feed-icon"
                                src={`/favicon/${feed.id}`}
                                alt=""
                                width="20"
                                height="20"
                                loading="lazy"
                              />
                              <div>
                                <strong class="truncate" title={titleHover}>
                                  {titleDisplay}
                                </strong>
                                {feed.description && (
                                  <div
                                    class="muted truncate"
                                    style="font-size: var(--font-size-sm); margin-top: 4px;"
                                    title={descHover}
                                  >
                                    {descDisplay}
                                  </div>
                                )}
                              </div>
                            </div>
                          </td>
                          <td>
                            <code>{feed.id}</code>
                          </td>
                          <td>
                            <CopyFieldInline value={emailAddress} />
                          </td>
                          <td>
                            <CopyFieldInline value={rssUrl} />
                          </td>
                          <td>
                            <CopyFieldInline value={atomUrl} />
                          </td>
                          <td>
                            {feed.expires_at ? (
                              <ExpiryBadge expiresAt={feed.expires_at} />
                            ) : (
                              <span class="muted">—</span>
                            )}
                          </td>
                          <td>
                            <div class="row-actions">
                              {isExpired ? (
                                <>
                                  <span
                                    class="button button-small button-disabled"
                                    aria-disabled="true"
                                    tabindex={-1}
                                  >
                                    Edit
                                  </span>
                                  <span
                                    class="button button-small button-disabled"
                                    aria-disabled="true"
                                    tabindex={-1}
                                  >
                                    Emails
                                  </span>
                                </>
                              ) : (
                                <>
                                  <a
                                    href={`/admin/feeds/${feed.id}/edit`}
                                    class="button button-small"
                                  >
                                    Edit
                                  </a>
                                  <a
                                    href={`/admin/feeds/${feed.id}/emails`}
                                    class="button button-small"
                                  >
                                    Emails
                                  </a>
                                </>
                              )}
                              <button
                                type="button"
                                class="button button-small button-danger button-delete"
                                data-delete-kind="feed"
                                data-feed-id={feed.id}
                                data-view="table"
                              >
                                Delete
                              </button>
                            </div>
                          </td>
                        </tr>
                      );
                    })}
                  </tbody>
                </table>
              </div>
            </form>
          </div>
        ) : (
          <>
            <div class="toolbar">
              <div class="toolbar-group toolbar-group-fill">
                <input
                  type="search"
                  id="feed-search"
                  class="search"
                  placeholder="Search title, feed id, or description"
                  oninput="scheduleFeedFilter()"
                />
                <span class="pill">Tip: use Table view for bulk deletion.</span>
              </div>
            </div>

            <ul class="feed-list">
              {feedsWithConfig.map((feed) => {
                const emailAddress = feedEmailAddress(feed.id, env);
                const rssUrl = feedRssUrl(feed.id, env);
                const atomUrl = feedAtomUrl(feed.id, env);
                const titleDisplay = clampText(feed.title, 140);
                const titleHover = clampText(feed.title, 1000);
                const descDisplay = clampText(feed.description || "", 240);
                const descHover = clampText(feed.description || "", 1000);
                const searchHaystack =
                  `${clampText(feed.title, 320)} ${feed.id} ${clampText(feed.description || "", 320)}`.toLowerCase();
                const isExpired =
                  feed.expires_at !== undefined &&
                  feed.expires_at <= Date.now();

                return (
                  <li
                    class={`feed-item card feed-row${isExpired ? " feed-expired" : ""}`}
                    data-feed-id={feed.id}
                    data-search={searchHaystack}
                  >
                    <div class="feed-header">
                      <h3 class="feed-title" title={titleHover}>
                        <img
                          class="feed-icon"
                          src={`/favicon/${feed.id}`}
                          alt=""
                          width="20"
                          height="20"
                          loading="lazy"
                        />
                        {titleDisplay}
                      </h3>
                      {feed.expires_at && (
                        <ExpiryBadge expiresAt={feed.expires_at} />
                      )}
                      {feed.description && (
                        <p class="feed-description">
                          <span title={descHover}>{descDisplay}</span>
                        </p>
                      )}
                    </div>

                    <div style="margin-bottom: var(--spacing-md);">
                      <div class="copyable">
                        <span class="copyable-label">Email:</span>
                        <div class="copyable-content">
                          <span
                            class="copyable-value"
                            data-copy={emailAddress}
                            title={emailAddress}
                          >
                            {emailAddress}
                          </span>
                          <div class="copy-icon-container">
                            <CopyIcon />
                            <CheckIcon />
                          </div>
                        </div>
                      </div>
                      <div class="copyable">
                        <span class="copyable-label">RSS Feed:</span>
                        <div class="copyable-content">
                          <span
                            class="copyable-value"
                            data-copy={rssUrl}
                            title={rssUrl}
                          >
                            {rssUrl}
                          </span>
                          <div class="copy-icon-container">
                            <CopyIcon />
                            <CheckIcon />
                          </div>
                        </div>
                      </div>
                      <div class="copyable">
                        <span class="copyable-label">Atom Feed:</span>
                        <div class="copyable-content">
                          <span
                            class="copyable-value"
                            data-copy={atomUrl}
                            title={atomUrl}
                          >
                            {atomUrl}
                          </span>
                          <div class="copy-icon-container">
                            <CopyIcon />
                            <CheckIcon />
                          </div>
                        </div>
                      </div>
                    </div>

                    <div class="feed-buttons">
                      <div class="feed-buttons-left">
                        {isExpired ? (
                          <>
                            <span
                              class="button button-small button-disabled"
                              aria-disabled="true"
                              tabindex={-1}
                            >
                              Edit
                            </span>
                            <span
                              class="button button-small button-disabled"
                              aria-disabled="true"
                              tabindex={-1}
                            >
                              Emails
                            </span>
                          </>
                        ) : (
                          <>
                            <a
                              href={`/admin/feeds/${feed.id}/edit`}
                              class="button button-small"
                            >
                              Edit
                            </a>
                            <a
                              href={`/admin/feeds/${feed.id}/emails`}
                              class="button button-small"
                            >
                              Emails
                            </a>
                          </>
                        )}
                      </div>
                      <div class="feed-buttons-right">
                        <button
                          type="button"
                          class="button button-small button-danger button-delete"
                          data-delete-kind="feed"
                          data-feed-id={feed.id}
                          data-view="list"
                        >
                          Delete
                        </button>
                      </div>
                    </div>
                  </li>
                );
              })}
            </ul>
          </>
        )}
      </div>

      <script dangerouslySetInnerHTML={{ __html: dashboardScript }} />
    </Layout>,
  );
});

// Mount sub-routers
app.route("/feeds", feedsRouter);
app.route("/", emailsRouter);

// Update feed via API (for in-place editing)
app.post(
  "/api/feeds/:feedId/update",
  zValidator(
    "json",
    updateFeedSchema.pick({ title: true, description: true }),
    (result, c) => {
      if (!result.success)
        return c.json({ success: false, error: result.error.issues }, 400);
    },
  ),
  async (c) => {
    const env = c.env;
    const feedId = c.req.param("feedId");

    try {
      const { title, description } = c.req.valid("json");

      // In-place edit: only title/description, expiry untouched.
      const result = await renameFeed(env, feedId, { title, description });

      if (result.status === "not_found") {
        return c.json({ error: "Feed not found" }, 404);
      }

      return c.json({ success: true });
    } catch (error) {
      logger.error("Error updating feed via API", { error: String(error) });
      return c.json({ error: "Error updating feed" }, 400);
    }
  },
);

// Export the Hono app
export const handle = app;
