import { describe, it, expect } from "vitest";
import { Hono } from "hono";
import { BaseLayout } from "../base.js";
import type { WebSession } from "../../lib/session.js";

const app = new Hono().get("/", (c) =>
  c.html(<BaseLayout title="Test Page">Page content</BaseLayout>)
);

describe("BaseLayout", () => {
  it("injects neobrutal tokens as :root CSS custom properties", async () => {
    const res = await app.request("/");
    const html = await res.text();
    expect(html).toContain(":root {");
    expect(html).toContain("--color-bg:");
    expect(html).toContain("--color-primary:");
  });

  it("loads reset.css and theme.css stylesheets", async () => {
    const res = await app.request("/");
    const html = await res.text();
    expect(html).toContain('href="/static/css/reset.css"');
    expect(html).toContain('href="/static/css/theme.css"');
  });

  it("loads Space Grotesk from Google Fonts", async () => {
    const res = await app.request("/");
    const html = await res.text();
    expect(html).toContain("fonts.googleapis.com");
    expect(html).toContain("Space+Grotesk");
  });

  it("renders semantic site-header, content-container, and site-footer", async () => {
    const res = await app.request("/");
    const html = await res.text();
    expect(html).toContain('class="site-header"');
    expect(html).toContain('class="content-container"');
    expect(html).toContain('class="site-footer"');
  });

  it("renders provided page title", async () => {
    const res = await app.request("/");
    const html = await res.text();
    expect(html).toContain("<title>Test Page</title>");
  });

  it("falls back to default title when none provided", async () => {
    const defaultApp = new Hono().get("/", (c) =>
      c.html(<BaseLayout>content</BaseLayout>)
    );
    const res = await defaultApp.request("/");
    const html = await res.text();
    expect(html).toContain("<title>atBB Forum</title>");
  });

  it("renders children inside content-container", async () => {
    const res = await app.request("/");
    const html = await res.text();
    expect(html).toContain("Page content");
  });

  it("renders header title link pointing to /", async () => {
    const res = await app.request("/");
    const html = await res.text();
    expect(html).toContain('href="/"');
    expect(html).toContain('class="site-header__title"');
  });

  describe("auth-aware navigation", () => {
    it("shows Log in link when auth is not provided (default unauthenticated)", async () => {
      const unauthApp = new Hono().get("/", (c) =>
        c.html(<BaseLayout>content</BaseLayout>)
      );
      const res = await unauthApp.request("/");
      const html = await res.text();
      expect(html).toContain('href="/login"');
      expect(html).toContain("Log in");
    });

    it("shows Log in link when auth is explicitly unauthenticated", async () => {
      const auth: WebSession = { authenticated: false };
      const unauthApp = new Hono().get("/", (c) =>
        c.html(<BaseLayout auth={auth}>content</BaseLayout>)
      );
      const res = await unauthApp.request("/");
      const html = await res.text();
      expect(html).toContain('href="/login"');
      expect(html).toContain("Log in");
      expect(html).not.toContain("Log out");
    });

    it("shows handle and Log out button when authenticated", async () => {
      const auth: WebSession = {
        authenticated: true,
        did: "did:plc:abc123",
        handle: "alice.bsky.social",
      };
      const authApp = new Hono().get("/", (c) =>
        c.html(<BaseLayout auth={auth}>content</BaseLayout>)
      );
      const res = await authApp.request("/");
      const html = await res.text();
      expect(html).toContain("alice.bsky.social");
      expect(html).toContain("Log out");
      expect(html).not.toContain('href="/login"');
    });

    it("renders logout as a form POST (not a link)", async () => {
      const auth: WebSession = {
        authenticated: true,
        did: "did:plc:abc123",
        handle: "alice.bsky.social",
      };
      const authApp = new Hono().get("/", (c) =>
        c.html(<BaseLayout auth={auth}>content</BaseLayout>)
      );
      const res = await authApp.request("/");
      const html = await res.text();
      // Logout must be a form POST for CSRF protection, not a plain link
      expect(html).toContain('action="/logout"');
      expect(html).toContain('method="post"');
      expect(html).toContain("Log out");
    });
  });

  describe("accessibility", () => {
    it("renders skip-to-content link before the site header", async () => {
      const res = await app.request("/");
      const html = await res.text();
      expect(html).toContain('class="skip-link"');
      expect(html).toContain('href="#main-content"');
      expect(html).toContain("Skip to main content");
      // Skip link must come before header in DOM order
      const skipLinkPos = html.indexOf("skip-link");
      const headerPos = html.indexOf("site-header");
      expect(skipLinkPos).toBeLessThan(headerPos);
    });

    it("renders main element with id for skip link target", async () => {
      const res = await app.request("/");
      const html = await res.text();
      expect(html).toContain('id="main-content"');
    });

    it("desktop nav has aria-label for Main navigation", async () => {
      const res = await app.request("/");
      const html = await res.text();
      expect(html).toContain('aria-label="Main navigation"');
    });

    it("mobile nav has distinct aria-label", async () => {
      const res = await app.request("/");
      const html = await res.text();
      expect(html).toContain('aria-label="Mobile navigation"');
    });
  });

  describe("favicon", () => {
    it("includes favicon link in head", async () => {
      const res = await app.request("/");
      const html = await res.text();
      expect(html).toContain('rel="icon"');
      expect(html).toContain("favicon.svg");
    });
  });

  describe("mobile navigation", () => {
    it("renders details/summary hamburger menu for mobile", async () => {
      const res = await app.request("/");
      const html = await res.text();
      expect(html).toContain("mobile-nav");
      expect(html).toContain("mobile-nav__toggle");
    });

    it("renders desktop nav separately from mobile nav", async () => {
      const res = await app.request("/");
      const html = await res.text();
      expect(html).toContain("desktop-nav");
    });

    it("hamburger has aria-label for accessibility", async () => {
      const res = await app.request("/");
      const html = await res.text();
      expect(html).toContain('aria-label="Menu"');
    });

    it("mobile nav contains login link when not authenticated", async () => {
      const res = await app.request("/");
      const html = await res.text();
      // Both mobile and desktop nav should have "Log in"
      const loginMatches = html.match(/Log in/g);
      expect(loginMatches!.length).toBe(2);
    });

    it("mobile nav contains auth state when logged in", async () => {
      const auth: WebSession = {
        authenticated: true,
        did: "did:plc:abc123",
        handle: "alice.bsky.social",
      };
      const authApp = new Hono().get("/", (c) =>
        c.html(<BaseLayout auth={auth}>content</BaseLayout>)
      );
      const res = await authApp.request("/");
      const html = await res.text();
      // Both mobile and desktop nav should have "Log out"
      const logoutMatches = html.match(/Log out/g);
      expect(logoutMatches!.length).toBe(2);
    });
  });
});
