import { Hono } from "hono";
import { BaseLayout } from "../layouts/base.js";
import { PageHeader, ErrorDisplay } from "../components/index.js";
import { getSession } from "../lib/session.js";
import { fetchApi } from "../lib/api.js";
import {
  isProgrammingError,
  isNotFoundError,
} from "../lib/errors.js";
import { logger } from "../lib/logger.js";
import { FALLBACK_THEME, type WebAppEnv } from "../lib/theme-resolution.js";

interface BoardResponse {
  id: string;
  did: string;
  uri: string;
  name: string;
  description: string | null;
  slug: string | null;
  sortOrder: number | null;
  categoryId: string;
  categoryUri: string | null;
  createdAt: string | null;
  indexedAt: string | null;
}

const CHAR_COUNTER_SCRIPT = `
  function updateCharCount(el) {
    var seg = new Intl.Segmenter();
    var n = Array.from(seg.segment(el.value)).length;
    var counter = document.getElementById("char-count");
    counter.textContent = (300 - n) + " left";
    counter.dataset.over = n > 300 ? "true" : "false";
  }
  function updateTitleCharCount(el) {
    var seg = new Intl.Segmenter();
    var n = Array.from(seg.segment(el.value)).length;
    var counter = document.getElementById("title-char-count");
    counter.textContent = (120 - n) + " left";
    counter.dataset.over = n > 120 ? "true" : "false";
  }
`;

export function createNewTopicRoutes(appviewUrl: string) {
  return new Hono<WebAppEnv>()
    .get("/new-topic", async (c) => {
      const resolvedTheme = c.get("theme") ?? FALLBACK_THEME;
      const boardIdParam = c.req.query("boardId");

      // boardId required and must be numeric
      if (!boardIdParam || !/^\d+$/.test(boardIdParam)) {
        return c.redirect("/");
      }

      const auth = await getSession(appviewUrl, c.req.header("cookie"));

      if (!auth.authenticated) {
        return c.html(
          <BaseLayout title="New Topic — atBB Forum" auth={auth} resolvedTheme={resolvedTheme}>
            <PageHeader title="New Topic" />
            <p>
              <a href="/login">Log in</a> to create a topic.
            </p>
          </BaseLayout>
        );
      }

      // Fetch board data — need the AT URI for the hidden form field
      let board: BoardResponse;
      try {
        board = await fetchApi<BoardResponse>(`/boards/${boardIdParam}`);
      } catch (error) {
        if (isProgrammingError(error)) throw error;

        if (isNotFoundError(error)) {
          return c.html(
            <BaseLayout title="Not Found — atBB Forum" auth={auth} resolvedTheme={resolvedTheme}>
              <ErrorDisplay message="This board doesn't exist." />
            </BaseLayout>,
            404
          );
        }

        logger.error("Failed to load board for new topic form", {
          operation: "GET /new-topic",
          boardId: boardIdParam,
          error: error instanceof Error ? error.message : String(error),
        });
        return c.redirect("/");
      }

      return c.html(
        <BaseLayout title="New Topic — atBB Forum" auth={auth} resolvedTheme={resolvedTheme}>
          <nav class="breadcrumb" aria-label="Breadcrumb">
            <ol>
              <li><a href="/">Home</a></li>
              <li><a href={`/boards/${board.id}`}>{board.name}</a></li>
              <li><span>New Topic</span></li>
            </ol>
          </nav>

          <PageHeader title="New Topic" description={`Posting to ${board.name}`} />

          <form
            hx-post="/new-topic"
            hx-target="#form-error"
            hx-swap="innerHTML"
            hx-indicator="#spinner"
            hx-disabled-elt="[type=submit]"
          >
            <input type="hidden" name="boardUri" value={board.uri} />
            <input type="hidden" name="boardId" value={board.id} />

            <div class="form-group">
              <label for="compose-title">Topic title</label>
              <input
                id="compose-title"
                name="title"
                type="text"
                placeholder="Give your topic a title"
                oninput="updateTitleCharCount(this)"
                aria-required="true"
                aria-describedby="title-char-count"
              />
              <div id="title-char-count" class="char-count" aria-live="polite">120 left</div>
            </div>

            <div class="form-group">
              <label for="compose-text">Your message</label>
              <textarea
                id="compose-text"
                name="text"
                rows={8}
                placeholder="What's on your mind?"
                oninput="updateCharCount(this)"
                aria-required="true"
                aria-describedby="char-count"
              />
              <div id="char-count" class="char-count" aria-live="polite">300 left</div>
            </div>

            <div id="form-error" role="alert" />

            <div class="form-actions">
              <button type="submit" class="btn btn-primary">
                Post Topic
                <span id="spinner" class="htmx-indicator">Posting…</span>
              </button>
            </div>
          </form>

          <script dangerouslySetInnerHTML={{ __html: CHAR_COUNTER_SCRIPT }} />
        </BaseLayout>
      );
    })
    .post("/new-topic", async (c) => {
      const cookieHeader = c.req.header("cookie") ?? "";

      // Parse URL-encoded form body (HTMX default encoding)
      let body: Record<string, string | File>;
      try {
        body = await c.req.parseBody();
      } catch (error) {
        logger.error("Failed to parse request body for POST /new-topic", {
          operation: "POST /new-topic",
          error: error instanceof Error ? error.message : String(error),
        });
        return c.html(<p class="form-error">Invalid form submission.</p>);
      }

      const { title, text, boardUri, boardId } = body;

      // Validate required fields before proxying
      if (typeof title !== "string" || !title.trim()) {
        return c.html(<p class="form-error">Topic title is required.</p>);
      }
      if (typeof text !== "string" || !text.trim()) {
        return c.html(<p class="form-error">Message text is required.</p>);
      }
      if (typeof boardUri !== "string" || !boardUri.trim()) {
        return c.html(
          <p class="form-error">Board information is missing. Please try again.</p>
        );
      }
      if (typeof boardId !== "string" || !/^\d+$/.test(boardId)) {
        return c.html(
          <p class="form-error">Board information is missing. Please try again.</p>
        );
      }

      // Proxy to AppView
      let appviewRes: Response;
      try {
        appviewRes = await fetch(`${appviewUrl}/api/topics`, {
          method: "POST",
          headers: {
            "Content-Type": "application/json",
            "Cookie": cookieHeader,
          },
          body: JSON.stringify({ title, text, boardUri }),
        });
      } catch (error) {
        if (isProgrammingError(error)) throw error;
        logger.error("Failed to proxy new topic to AppView", {
          operation: "POST /new-topic",
          error: error instanceof Error ? error.message : String(error),
        });
        return c.html(
          <p class="form-error">Forum temporarily unavailable. Please try again.</p>
        );
      }

      // Success: instruct HTMX to navigate to the board with a flash message
      if (appviewRes.status === 201) {
        const headers = new Headers();
        headers.set("HX-Redirect", `/boards/${boardId}?posted=1`);
        return new Response(null, { status: 200, headers });
      }

      // Map AppView error to user-friendly message
      let errorMessage = "Something went wrong. Please try again.";

      if (appviewRes.status === 401) {
        errorMessage = "You must be logged in to post.";
      } else if (appviewRes.status === 403) {
        try {
          const errBody = (await appviewRes.json()) as { error?: string };
          const msg = errBody.error ?? "";
          if (msg.toLowerCase().includes("banned")) {
            errorMessage = "You are banned from this forum.";
          } else {
            errorMessage = msg || "You are not allowed to create topics.";
          }
        } catch {
          logger.error("Failed to parse AppView 403 response body for new topic", {
            operation: "POST /new-topic",
          });
          errorMessage = "You are not allowed to create topics.";
        }
      } else if (appviewRes.status === 400) {
        try {
          const errBody = (await appviewRes.json()) as { error?: string };
          errorMessage = errBody.error ?? errorMessage;
        } catch {
          logger.error("Failed to parse AppView 400 response body for new topic", {
            operation: "POST /new-topic",
          });
        }
      } else if (appviewRes.status >= 400 && appviewRes.status < 500) {
        logger.error("AppView returned unexpected client error for new topic", {
          operation: "POST /new-topic",
          status: appviewRes.status,
        });
      } else if (appviewRes.status >= 500) {
        logger.error("AppView returned server error for new topic", {
          operation: "POST /new-topic",
          status: appviewRes.status,
        });
      }

      return c.html(<p class="form-error">{errorMessage}</p>);
    });
}
