import { test, expect } from "../../base.js";
import { login } from "../../helpers.js";
import { MockServer } from "../../mockServer.js";
import {
  createConvo,
  createMessage,
  createPost,
  createProfile,
} from "../../../shared/factories.js";

const BLACKSKY_APPVIEW_DID = "did:web:api.blacksky.community#bsky_appview";
const BLACKSKY_CHAT_DID = "did:web:api.blacksky.community#bsky_chat";
const BLUESKY_APPVIEW_DID = "did:web:api.bsky.app#bsky_appview";
const BLUESKY_CHAT_DID = "did:web:api.bsky.chat#bsky_chat";

async function seedAppViewConfig(page, config) {
  // Seed on the first page load only; otherwise addInitScript would re-seed
  // on every subsequent navigation and defeat things like the reset flow.
  await page.addInitScript((seeded) => {
    if (sessionStorage.getItem("test-appview-seeded") === "true") return;
    localStorage.setItem("appview-config", JSON.stringify(seeded));
    sessionStorage.setItem("test-appview-seeded", "true");
  }, config);
}

function collectProxyHeaders(page, pathFragment) {
  const headers = [];
  page.on("request", (request) => {
    if (request.url().includes(pathFragment)) {
      const header = request.headers()["atproto-proxy"];
      if (header) {
        headers.push(header);
      }
    }
  });
  return headers;
}

function buildTimelineMockServer() {
  const mockServer = new MockServer();
  const author = createProfile({
    did: "did:plc:someone",
    handle: "someone.bsky.social",
    displayName: "Someone",
  });
  mockServer.addProfile(author);
  mockServer.addTimelinePosts([
    createPost({
      uri: "at://did:plc:someone/app.bsky.feed.post/post1",
      text: "Hello",
      authorHandle: author.handle,
      authorDisplayName: author.displayName,
    }),
  ]);
  return mockServer;
}

test.describe("Switch app view flow", () => {
  test("uses the stored app view DID as the atproto-proxy header on app.bsky requests", async ({
    page,
  }) => {
    const mockServer = buildTimelineMockServer();
    await mockServer.setup(page);

    const appProxyHeaders = collectProxyHeaders(page, "/xrpc/app.bsky.");

    await seedAppViewConfig(page, {
      id: "blacksky",
      appViewServiceDid: BLACKSKY_APPVIEW_DID,
      chatServiceDid: BLACKSKY_CHAT_DID,
    });
    await login(page);

    await page.goto("/");
    await expect(
      page.locator("#home-view").locator('[data-testid="feed-item"]'),
    ).toHaveCount(1, { timeout: 10000 });

    expect(appProxyHeaders.length).toBeGreaterThan(0);
    for (const header of appProxyHeaders) {
      expect(header).toBe(BLACKSKY_APPVIEW_DID);
    }
  });

  test("uses the stored chat DID as the atproto-proxy header on chat.bsky requests", async ({
    page,
  }) => {
    const mockServer = new MockServer();
    const otherMember = createProfile({
      did: "did:plc:alice1",
      handle: "alice.bsky.social",
      displayName: "Alice",
    });
    const lastMessage = createMessage({
      id: "msg-1",
      text: "Hello",
      senderDid: otherMember.did,
      sentAt: "2025-01-15T12:00:00.000Z",
    });
    mockServer.addConvos([
      createConvo({ id: "convo-1", otherMember, lastMessage }),
    ]);
    await mockServer.setup(page);

    const chatProxyHeaders = collectProxyHeaders(page, "/xrpc/chat.bsky.");

    await seedAppViewConfig(page, {
      id: "blacksky",
      appViewServiceDid: BLACKSKY_APPVIEW_DID,
      chatServiceDid: BLACKSKY_CHAT_DID,
    });
    await login(page);

    await page.goto("/messages");
    await expect(page.locator("#chat-view .convo-item")).toHaveCount(1, {
      timeout: 10000,
    });

    expect(chatProxyHeaders.length).toBeGreaterThan(0);
    for (const header of chatProxyHeaders) {
      expect(header).toBe(BLACKSKY_CHAT_DID);
    }
  });

  test("reset-appview query param clears the stored config and strips the param", async ({
    page,
  }) => {
    const mockServer = new MockServer();
    await mockServer.setup(page);

    await seedAppViewConfig(page, {
      id: "blacksky",
      appViewServiceDid: BLACKSKY_APPVIEW_DID,
      chatServiceDid: BLACKSKY_CHAT_DID,
    });

    await page.goto("/?reset-appview=1");

    await expect
      .poll(() => page.evaluate(() => localStorage.getItem("appview-config")))
      .toBeNull();

    const currentUrl = new URL(page.url());
    expect(currentUrl.searchParams.has("reset-appview")).toBe(false);
  });

  test("reverts to the Bluesky defaults after reset", async ({ page }) => {
    const mockServer = buildTimelineMockServer();
    const otherMember = createProfile({
      did: "did:plc:alice1",
      handle: "alice.bsky.social",
      displayName: "Alice",
    });
    mockServer.addConvos([
      createConvo({
        id: "convo-1",
        otherMember,
        lastMessage: createMessage({
          id: "msg-1",
          text: "Hello",
          senderDid: otherMember.did,
          sentAt: "2025-01-15T12:00:00.000Z",
        }),
      }),
    ]);
    await mockServer.setup(page);

    const appProxyHeaders = collectProxyHeaders(page, "/xrpc/app.bsky.");
    const chatProxyHeaders = collectProxyHeaders(page, "/xrpc/chat.bsky.");

    await seedAppViewConfig(page, {
      id: "blacksky",
      appViewServiceDid: BLACKSKY_APPVIEW_DID,
      chatServiceDid: BLACKSKY_CHAT_DID,
    });
    await login(page);

    // Only assert on requests made after the reset; wait for any lingering
    // requests from the /login page to complete before clearing.
    await page.waitForLoadState("networkidle");
    appProxyHeaders.length = 0;
    chatProxyHeaders.length = 0;
    await page.goto("/?reset-appview=1");
    await expect(
      page.locator("#home-view").locator('[data-testid="feed-item"]'),
    ).toHaveCount(1, { timeout: 10000 });

    await page.goto("/messages");
    await expect(page.locator("#chat-view .convo-item")).toHaveCount(1, {
      timeout: 10000,
    });

    expect(appProxyHeaders.length).toBeGreaterThan(0);
    for (const header of appProxyHeaders) {
      expect(header).toBe(BLUESKY_APPVIEW_DID);
    }
    expect(chatProxyHeaders.length).toBeGreaterThan(0);
    for (const header of chatProxyHeaders) {
      expect(header).toBe(BLUESKY_CHAT_DID);
    }
  });

  test("persists the selected App View to localStorage when the form is submitted", async ({
    page,
  }) => {
    const mockServer = new MockServer();
    await mockServer.setup(page);

    await page.goto("/login");
    await page.locator("#login-advanced summary").click();
    await page.locator('select[name="appview"]').selectOption("blacksky");
    await page.locator('input[name="handle"]').fill("someone.bsky.social");
    await page.locator('button[type="submit"]').click();

    await expect
      .poll(() =>
        page.evaluate(() => {
          const raw = localStorage.getItem("appview-config");
          return raw ? JSON.parse(raw) : null;
        }),
      )
      .toEqual({
        id: "blacksky",
        appViewServiceDid: BLACKSKY_APPVIEW_DID,
        chatServiceDid: BLUESKY_CHAT_DID,
      });
  });
});
