// Cloud-specific (browser): switching organizations changes the active workspace.
// A fresh user creates two organizations through the real web UI — the first
// via onboarding and the second via the account-menu → org switcher → "Create
// organization" modal — then uses the same switcher to return to the first org
// and confirms the workspace label in the bottom-left account button updates.
import { expect } from "@effect/vitest";
import { Effect } from "effect";

import { scenario } from "../src/scenario";
import { Browser, Target } from "../src/services";

scenario(
  "Organizations · switching organizations switches the workspace",
  {},
  Effect.gen(function* () {
    const target = yield* Target;
    const browser = yield* Browser;
    const identity = yield* target.newIdentity({ org: false });

    yield* browser.session(identity, async ({ page, step }) => {
      // ── Step 1: onboarding, create the first org ─────────────────────
      await step("Fresh user lands on onboarding (no organization yet)", async () => {
        await page.goto("/", { waitUntil: "networkidle" });
        await page.getByPlaceholder("Northwind Labs").waitFor();
      });

      const ORG_1 = "Switcher Org One";
      const ORG_2 = "Switcher Org Two";

      await step(`Create "${ORG_1}" via onboarding`, async () => {
        await page.getByPlaceholder("Northwind Labs").fill(ORG_1);
        await page.getByRole("button", { name: "Create organization" }).click();
        // Onboarding step 2 — proves the first org was created.
        await page.getByText("Connect your MCP client").waitFor();
      });

      await step("Continue into the app", async () => {
        await page.getByRole("button", { name: "Continue to app" }).click();
        await page.getByText("Integrations").first().waitFor();
        // Let the router navigation fully settle before opening menus — a late
        // remount closes them mid-interaction. The console canonicalizes onto
        // the org's URL slug (/switcher-org-one).
        await page.waitForURL((url) => /^\/[a-z0-9-]+\/?$/.test(url.pathname), {
          timeout: 30_000,
        });
        await page.waitForLoadState("networkidle");
      });

      // ── Step 2: create the second org via the account-menu switcher ──
      await step('Open the org switcher and choose "Create organization"', async () => {
        // Bounded retry: under parallel-suite load the radix menu re-renders
        // while the org list loads and a click can land on a closing menu.
        for (let attempt = 1; ; attempt++) {
          try {
            await page.keyboard.press("Escape");
            await page.getByRole("button", { name: /Test User/ }).click();
            await page.getByRole("menuitem", { name: ORG_1 }).click({ timeout: 5_000 });
            const subContent = page.locator('[data-slot="dropdown-menu-sub-content"]');
            await subContent.waitFor({ state: "visible", timeout: 5_000 });
            await subContent
              .getByText("Create organization", { exact: true })
              .click({ timeout: 5_000 });
            await page.getByText("Add another organization").waitFor({ timeout: 5_000 });
            break;
          } catch (error) {
            if (attempt >= 3) throw error;
          }
        }
      });

      await step(`Create "${ORG_2}" via the org switcher modal`, async () => {
        await page.getByPlaceholder("Northwind Labs").fill(ORG_2);
        await page.getByRole("button", { name: "Create organization" }).click();
        // The modal closes and the session switches into the new org.
        await page.getByText("Add another organization").waitFor({ state: "hidden" });
        // Confirm the account button now shows ORG_2.
        await page.getByRole("button", { name: new RegExp(ORG_2) }).waitFor();
      });

      // Capture the label while we are in ORG_2 as a baseline.
      const labelAfterOrg2 = await page
        .getByRole("button", { name: new RegExp(ORG_2) })
        .innerText();
      expect(labelAfterOrg2, "account button shows the second org after creation").toContain(ORG_2);

      // ── Step 3: switch back to the first org ─────────────────────────
      // The org-switcher sub-menu shows org IDs (not names) because the stub's
      // getOrganization returns the ID as the name. The currently-active org is
      // rendered with data-disabled="" (Radix convention). The only item without
      // data-disabled that isn't "Create organization" is ORG_1.
      await step(`Open the org switcher and switch back to "${ORG_1}"`, async () => {
        await page.waitForLoadState("networkidle");
        await page.getByRole("button", { name: /Test User/ }).click();
        // Click the SubTrigger (shows current org name = ORG_2) to expand the list.
        await page.getByRole("menuitem", { name: ORG_2 }).click();
        // Wait for the sub-content to open.
        await page.locator('[data-slot="dropdown-menu-sub-content"]').waitFor({ state: "visible" });
        // The organizationsAtom loads asynchronously — wait until the loading state
        // clears and the org items appear. The org items have data-disabled="" when
        // active and no data-disabled when not. "Create organization" is always shown
        // and always enabled; wait until there are at least 2 non-disabled items
        // (the non-active org + "Create organization") before clicking.
        await page
          .locator('[data-slot="dropdown-menu-sub-content"]')
          .locator('[role="menuitem"]:not([data-disabled])')
          .nth(1)
          .waitFor();
        // Now the sub-content has loaded. The org items appear BEFORE the separator and
        // "Create organization". ORG_1 (non-active, not disabled) appears before ORG_2
        // (active, disabled) and before "Create organization". Click the first
        // non-disabled item that is NOT "Create organization" — that is ORG_1.
        await page
          .locator('[data-slot="dropdown-menu-sub-content"]')
          .locator('[role="menuitem"]:not([data-disabled])')
          .filter({ hasNot: page.getByText("Create organization") })
          .first()
          .click();
        // The menu closes, the page reloads, and the session switches into ORG_1.
        await page.getByRole("button", { name: new RegExp(ORG_1) }).waitFor();
      });

      // ── Assert: workspace label reflects the first org ───────────────
      const labelAfterSwitch = await page
        .getByRole("button", { name: new RegExp(ORG_1) })
        .innerText();
      expect(labelAfterSwitch, "account button shows the first org after switching back").toContain(
        ORG_1,
      );

      // Cross-check the active org through the session API.
      const cookie = (await page.context().cookies()).map((c) => `${c.name}=${c.value}`).join("; ");
      const response = await fetch(new URL("/api/auth/organizations", target.baseUrl), {
        headers: { cookie },
      });
      const body = (await response.json()) as {
        organizations: ReadonlyArray<{ name: string }>;
        activeOrganizationId?: string;
      };
      expect(response.ok).toBe(true);
      expect(body.organizations.length, "exactly two organizations exist for this user").toBe(2);
    });
  }),
);
