// @vitest-environment jsdom

import { cleanup, fireEvent, render, screen, waitFor, within } from "@testing-library/react";
import { beforeEach, describe, expect, it, vi } from "vitest";
import RegionsPage from "./page";
import { mockRegionsApi } from "./test-helpers";

vi.mock("@/lib/gray-release", () => ({
  grayReleaseConfig: {
    label: "Prod Gray",
    presets: [
      {
        key: "nextgen",
        label: "Nextgen",
        regionIds: ["r-dev-1", "r-dev-2", "missing-region"],
      },
      {
        key: "serverless",
        label: "Serverless",
        regionIds: ["r-prod-1"],
      },
      {
        key: "dedicated",
        label: "Dedicated",
        regionIds: ["r-staging-1"],
      },
    ],
  },
}));

const regionsPayload = {
  ok: true,
  data: {
    regions: [
      {
        id: "r-dev-1",
        architecture: "nextgen",
        environment: "dev",
        provider: "aws",
        regionName: "dev-1",
        directoryPath: "envs/dev-aws-dev-1",
        currentTag: "sha1",
        imageRegistry: "registry",
        componentTags: { acs: "sha1", vector: "v1", vmagent: "m1" },
        sectionComponentTags: {
          k8s: { acs: null, vector: "v1-k8s", vmagent: "m1-k8s" },
          tidbcluster: { acs: null, vector: null, vmagent: null },
          sharedpool: { acs: null, vector: "v1-sp", vmagent: "m1-sp" },
          tidbgroup: { acs: null, vector: "v1-tg", vmagent: null },
        },
        syncStatus: "enabled",
        syncStatusSource:
          "dev/nextgen-regional-control-plane/app-registry/o11y-agent-control-server.yaml",
      },
      {
        id: "r-staging-1",
        architecture: "dedicated",
        environment: "staging",
        provider: "gcp",
        regionName: "staging-1",
        directoryPath: "envs/staging-gcp-staging-1",
        currentTag: "sha2",
        imageRegistry: "registry",
        componentTags: { acs: "sha2", vector: "v2", vmagent: "m2" },
        sectionComponentTags: {
          k8s: { acs: null, vector: "v2-k8s", vmagent: "m2-k8s" },
          tidbcluster: { acs: null, vector: "v2-tc", vmagent: "m2-tc" },
          sharedpool: { acs: null, vector: null, vmagent: null },
          tidbgroup: { acs: null, vector: null, vmagent: null },
        },
        syncStatus: "disabled",
        syncStatusSource: "app-registry/o11y-agent-control-server.yaml",
      },
      {
        id: "r-prod-1",
        architecture: "serverless",
        environment: "prod",
        provider: "azure",
        regionName: "prod-1",
        directoryPath: "envs/prod-azure-prod-1",
        currentTag: "sha3",
        imageRegistry: "registry",
        componentTags: { acs: "sha3", vector: "v3", vmagent: "m3" },
        sectionComponentTags: {
          k8s: { acs: null, vector: "v3-k8s", vmagent: "m3-k8s" },
          tidbcluster: { acs: null, vector: null, vmagent: null },
          sharedpool: { acs: null, vector: "v3-sp", vmagent: "m3-sp" },
          tidbgroup: { acs: null, vector: "v3-tg", vmagent: null },
        },
        syncStatus: "unknown",
        syncStatusSource:
          "nextgen-regional-control-plane/app-registry/o11y-agent-control-server.yaml",
        syncStatusReason: "yaml parse error",
      },
      {
        id: "r-dev-2",
        architecture: "nextgen",
        environment: "dev",
        provider: "alicloud",
        regionName: "dev-2",
        directoryPath: "envs/dev-alicloud-dev-2",
        currentTag: "sha4",
        imageRegistry: "registry",
        componentTags: { acs: "sha4", vector: "v4", vmagent: "m4" },
        sectionComponentTags: {
          k8s: { acs: null, vector: "v4-k8s", vmagent: "m4-k8s" },
          tidbcluster: { acs: null, vector: null, vmagent: null },
          sharedpool: { acs: null, vector: "v4-sp", vmagent: "m4-sp" },
          tidbgroup: { acs: null, vector: "v4-tg", vmagent: null },
        },
        syncStatus: "enabled",
        syncStatusSource:
          "dev/nextgen-regional-control-plane/app-registry/o11y-agent-control-server.yaml",
      },
    ],
  },
};

describe("RegionsPage environment filter", () => {
  beforeEach(() => {
    cleanup();
    vi.restoreAllMocks();
    mockRegionsApi(regionsPayload);
  });

  it("keeps component filter and refresh adjacent while search fills the toolbar", async () => {
    render(<RegionsPage />);

    await waitFor(() => {
      expect(screen.getByText("Regions")).toBeInTheDocument();
    });

    const searchInput = screen.getByLabelText("Search regions");
    const refreshButton = screen.getByRole("button", { name: "Refresh" });
    const componentFilter = screen.getByLabelText("Filter by component");

    expect(searchInput.closest("section")).toHaveClass("w-full");
    expect(searchInput.parentElement).toHaveClass("flex-1", "min-w-64");
    expect(componentFilter.compareDocumentPosition(refreshButton)).toBe(Node.DOCUMENT_POSITION_FOLLOWING);
    expect(refreshButton).toHaveClass(
      "h-8",
      "w-8",
      "rounded-md",
      "border",
      "border-accent/30",
      "bg-accent-muted",
      "text-accent",
      "font-medium",
      "hover:bg-accent/15"
    );
    expect(refreshButton).not.toHaveClass("ml-auto");
    expect(refreshButton).not.toHaveClass("translate-y-0.5");
  });

  it("labels each selection filter and removes redundant dropdown helper text", async () => {
    render(<RegionsPage />);

    await waitFor(() => {
      expect(screen.getByText("dev-1")).toBeInTheDocument();
    });

    expect(screen.getByLabelText("Filter by architecture")).toHaveTextContent(
      "ArchitecturesAll"
    );
    expect(screen.getByLabelText("Filter by environment")).toHaveTextContent(
      "EnvironmentsAll"
    );
    expect(screen.getByLabelText("Filter by provider")).toHaveTextContent("ProvidersAll");
    expect(screen.getByLabelText("Select gray release preset")).toHaveTextContent(
      "Gray ReleaseUnset"
    );
    expect(screen.getByLabelText("Filter by component")).toHaveTextContent(
      "ComponentACS"
    );
    expect(screen.getByLabelText("Filter by component").closest("details")).not.toBeNull();
    expect(screen.getByLabelText("Filter by architecture").closest("details")?.parentElement).toHaveClass(
      "min-w-44"
    );
    expect(screen.getByLabelText("Filter by environment").closest("details")?.parentElement).toHaveClass(
      "min-w-44"
    );
    expect(screen.getByLabelText("Filter by provider").closest("details")?.parentElement).toHaveClass(
      "min-w-40"
    );
    expect(screen.getByLabelText("Select gray release preset").closest("details")?.parentElement).toHaveClass(
      "min-w-44"
    );
    expect(screen.getByLabelText("Filter by component").closest("details")?.parentElement).toHaveClass(
      "w-44"
    );

    fireEvent.click(screen.getByLabelText("Filter by architecture"));
    expect(screen.getByLabelText("Architecture dedicated").closest(".absolute")).toHaveClass(
      "w-44"
    );
    fireEvent.click(screen.getByLabelText("Filter by environment"));
    fireEvent.click(screen.getByLabelText("Filter by provider"));
    fireEvent.click(screen.getByLabelText("Select gray release preset"));

    expect(screen.queryByText("Choose one or more architectures")).not.toBeInTheDocument();
    expect(screen.queryByText("Choose one or more environments")).not.toBeInTheDocument();
    expect(screen.queryByText("Choose one or more providers")).not.toBeInTheDocument();
    expect(screen.queryByText("Select built-in gray release regions")).not.toBeInTheDocument();
  });

  it("renders the regions table with history-aligned data table styling", async () => {
    const { container } = render(<RegionsPage />);

    await waitFor(() => {
      expect(screen.getByText("dev-1")).toBeInTheDocument();
    });

    const table = container.querySelector("table");
    const row = screen.getByText("dev-1").closest("tr");
    expect(table).toHaveClass("w-full", "text-sm");
    expect(row).toHaveClass("border-b", "border-surface-2/50", "hover:bg-surface-1");

    expect(screen.getByRole("columnheader", { name: "" })).toHaveClass(
      "px-5",
      "py-2.5"
    );
    expect(screen.getByRole("columnheader", { name: "Architecture" })).toHaveClass(
      "px-5",
      "py-2.5",
      "text-xs",
      "font-semibold",
      "uppercase",
      "tracking-wider",
      "text-text-muted"
    );
    expect(screen.getByRole("columnheader", { name: "Tag (ACS)" })).toHaveClass(
      "px-5",
      "py-2.5"
    );

    expect(screen.getByLabelText("Select dev-1").closest("td")).toHaveClass(
      "px-5",
      "py-2.5"
    );
    const architectureBadge = within(row!).getByText("nextgen");
    expect(architectureBadge.closest("td")).toHaveClass("px-5", "py-2.5");
    expect(architectureBadge).toHaveClass(
      "inline-flex",
      "items-center",
      "rounded-md",
      "border",
      "px-2",
      "py-0.5",
      "text-xs",
      "font-medium",
      "uppercase",
      "bg-arch-nextgen/10",
      "text-arch-nextgen",
      "border-arch-nextgen/30"
    );
    expect(architectureBadge).not.toHaveClass(
      "rounded-r",
      "border-l-2",
      "text-purple-400"
    );
    expect(architectureBadge).not.toHaveClass("leading-4");

    expect(within(row!).getByText("dev").closest("td")).toHaveClass(
      "px-5",
      "py-2.5",
      "text-text-secondary"
    );
    expect(within(row!).getByText("aws").closest("td")).toHaveClass(
      "px-5",
      "py-2.5",
      "text-text-secondary"
    );
    expect(within(row!).getByText("dev-1")).toHaveClass(
      "font-mono",
      "text-[13px]",
      "text-accent"
    );

    const grayBadge = screen.getByLabelText("dev-1 is in Nextgen gray release preset");
    expect(grayBadge).toHaveClass(
      "inline-flex",
      "items-center",
      "rounded-md",
      "border",
      "px-2",
      "py-0.5",
      "text-xs",
      "font-medium",
      "uppercase",
      "border-text-muted/30",
      "bg-text-muted/10",
      "text-text-muted"
    );
    expect(grayBadge).not.toHaveClass("text-[10px]", "font-semibold");
    expect(grayBadge).not.toHaveClass("leading-4");

    const syncBadge = within(row!).getByText("Enabled");
    expect(syncBadge.closest("td")).toHaveClass("px-5", "py-2.5");
    expect(syncBadge).toHaveClass(
      "inline-flex",
      "items-center",
      "rounded-md",
      "border",
      "px-2",
      "py-0.5",
      "text-xs",
      "font-medium",
      "uppercase",
      "bg-success/10",
      "text-success",
      "border-success/30"
    );
    expect(syncBadge).not.toHaveClass("leading-4");

    expect(within(row!).getByText("sha1").closest("td")).toHaveClass(
      "px-5",
      "py-2.5",
      "font-mono",
      "text-[13px]",
      "text-text-secondary"
    );
  });

  it("supports selecting multiple environments", async () => {
    render(<RegionsPage />);

    await waitFor(() => {
      expect(screen.getByText("dev-1")).toBeInTheDocument();
      expect(screen.getByText("staging-1")).toBeInTheDocument();
      expect(screen.getByText("prod-1")).toBeInTheDocument();
      expect(screen.getByText("dev-2")).toBeInTheDocument();
    });

    fireEvent.click(screen.getByLabelText("Filter by environment"));
    fireEvent.click(screen.getByLabelText("Environment staging"));
    fireEvent.click(screen.getByLabelText("Environment dev"));

    expect(screen.getByText("dev-1")).toBeInTheDocument();
    expect(screen.getByText("staging-1")).toBeInTheDocument();
    expect(screen.getByText("dev-2")).toBeInTheDocument();
    expect(screen.queryByText("prod-1")).not.toBeInTheDocument();
  });

  it("supports selecting multiple architectures and providers", async () => {
    render(<RegionsPage />);

    await waitFor(() => {
      expect(screen.getByText("dev-1")).toBeInTheDocument();
      expect(screen.getByText("staging-1")).toBeInTheDocument();
      expect(screen.getByText("prod-1")).toBeInTheDocument();
      expect(screen.getByText("dev-2")).toBeInTheDocument();
    });

    fireEvent.click(screen.getByLabelText("Filter by architecture"));
    fireEvent.click(screen.getByLabelText("Architecture nextgen"));
    fireEvent.click(screen.getByLabelText("Architecture dedicated"));

    fireEvent.click(screen.getByLabelText("Filter by provider"));
    fireEvent.click(screen.getByLabelText("Provider aws"));
    fireEvent.click(screen.getByLabelText("Provider gcp"));

    expect(screen.getByText("dev-1")).toBeInTheDocument();
    expect(screen.getByText("staging-1")).toBeInTheDocument();
    expect(screen.queryByText("prod-1")).not.toBeInTheDocument();
    expect(screen.queryByText("dev-2")).not.toBeInTheDocument();
  });

  it("shows gray release in the former sync status filter position", async () => {
    render(<RegionsPage />);

    await waitFor(() => {
      expect(screen.getByText("dev-1")).toBeInTheDocument();
    });

    expect(screen.getByLabelText("Select gray release preset")).toBeInTheDocument();
  });

  it("selects configured gray release regions by deployment model", async () => {
    render(<RegionsPage />);

    await waitFor(() => {
      expect(screen.getByText("dev-1")).toBeInTheDocument();
    });

    fireEvent.click(screen.getByLabelText("Select gray release preset"));
    fireEvent.click(screen.getByRole("button", { name: "Select gray release Nextgen" }));

    expect(screen.getByLabelText("Select dev-1")).toBeChecked();
    expect(screen.getByLabelText("Select dev-2")).toBeChecked();
    expect(screen.getByLabelText("Select staging-1")).not.toBeChecked();
    expect(screen.getByLabelText("Select prod-1")).not.toBeChecked();
    expect(screen.getByText("2 selected")).toBeInTheDocument();
  });

  it("links selected regions to the observe page", async () => {
    render(<RegionsPage />);

    await waitFor(() => {
      expect(screen.getByText("dev-1")).toBeInTheDocument();
    });

    fireEvent.click(screen.getByLabelText("Select dev-1"));
    fireEvent.click(screen.getByLabelText("Select dev-2"));

    const bumpLink = screen.getByRole("link", { name: "Bump" });
    expect(bumpLink).toHaveAttribute(
      "href",
      `/bump?regions=${encodeURIComponent("r-dev-1,r-dev-2")}`
    );
    const observeLink = screen.getByRole("link", { name: "Observe" });
    expect(observeLink).toHaveAttribute(
      "href",
      `/observe?regions=${encodeURIComponent("r-dev-1,r-dev-2")}&component=acs`
    );
    const agentsLink = screen.getByRole("link", { name: "Agents" });
    expect(agentsLink).toHaveAttribute(
      "href",
      `/agents?regions=${encodeURIComponent("r-dev-1,r-dev-2")}`
    );
  });

  it("only shows navigation actions after regions are selected", async () => {
    render(<RegionsPage />);

    await waitFor(() => {
      expect(screen.getByText("dev-1")).toBeInTheDocument();
    });

    fireEvent.click(screen.getByLabelText("Select dev-1"));

    const bumpLink = screen.getByRole("link", { name: "Bump" });
    const observeLink = screen.getByRole("link", { name: "Observe" });
    const agentsLink = screen.getByRole("link", { name: "Agents" });

    expect(screen.queryByRole("button", { name: "Select All" })).not.toBeInTheDocument();
    expect(screen.queryByRole("button", { name: "Deselect All" })).not.toBeInTheDocument();
    expect(bumpLink).toHaveClass("h-8", "px-3", "text-sm", "bg-accent");
    expect(observeLink).toHaveClass("h-8", "px-3", "text-sm", "bg-accent-muted");
    expect(agentsLink).toHaveClass("h-8", "px-3", "text-sm", "bg-accent-muted");
  });

  it("marks configured gray release regions with preset badges next to region names", async () => {
    render(<RegionsPage />);

    await waitFor(() => {
      expect(screen.getByText("dev-1")).toBeInTheDocument();
      expect(screen.getByText("dev-2")).toBeInTheDocument();
      expect(screen.getByText("staging-1")).toBeInTheDocument();
      expect(screen.getByText("prod-1")).toBeInTheDocument();
    });

    expect(screen.getByLabelText("dev-1 is in Nextgen gray release preset")).toHaveTextContent(
      "Gray"
    );
    expect(screen.getByLabelText("dev-2 is in Nextgen gray release preset")).toHaveTextContent(
      "Gray"
    );
    expect(
      screen.getByLabelText("staging-1 is in Dedicated gray release preset")
    ).toHaveTextContent("Gray");
    expect(screen.getByLabelText("prod-1 is in Serverless gray release preset")).toHaveTextContent(
      "Gray"
    );
    expect(screen.queryByText("missing-region")).not.toBeInTheDocument();
  });

  it("selects gray release regions from the current filtered result", async () => {
    render(<RegionsPage />);

    await waitFor(() => {
      expect(screen.getByText("dev-1")).toBeInTheDocument();
      expect(screen.getByText("dev-2")).toBeInTheDocument();
    });

    fireEvent.click(screen.getByLabelText("Filter by provider"));
    fireEvent.click(screen.getByLabelText("Provider aws"));

    expect(screen.getByText("dev-1")).toBeInTheDocument();
    expect(screen.queryByText("dev-2")).not.toBeInTheDocument();

    fireEvent.click(screen.getByLabelText("Select gray release preset"));
    fireEvent.click(screen.getByRole("button", { name: "Select gray release Nextgen" }));

    expect(screen.getByLabelText("Select dev-1")).toBeChecked();
    expect(screen.getByText("of 1 shown")).toBeInTheDocument();
    expect(screen.queryByText("2 selected")).not.toBeInTheDocument();
  });

  it("closes opened dropdown when clicking outside", async () => {
    render(<RegionsPage />);

    await waitFor(() => {
      expect(screen.getByText("dev-1")).toBeInTheDocument();
    });

    const archSummary = screen.getByLabelText("Filter by architecture");
    fireEvent.click(archSummary);

    const archDetails = archSummary.closest("details");
    expect(archDetails?.open).toBe(true);

    fireEvent.pointerDown(document.body);

    expect(archDetails?.open).toBe(false);
  });

  it("closes the previous dropdown when opening another filter", async () => {
    render(<RegionsPage />);

    await waitFor(() => {
      expect(screen.getByText("dev-1")).toBeInTheDocument();
    });

    const archSummary = screen.getByLabelText("Filter by architecture");
    const envSummary = screen.getByLabelText("Filter by environment");
    fireEvent.click(archSummary);

    const archDetails = archSummary.closest("details");
    const envDetails = envSummary.closest("details");
    expect(archDetails?.open).toBe(true);

    fireEvent.click(envSummary);

    expect(archDetails?.open).toBe(false);
    expect(envDetails?.open).toBe(true);
  });

  it("keeps dropdown open when clicking inside dropdown content", async () => {
    render(<RegionsPage />);

    await waitFor(() => {
      expect(screen.getByText("dev-1")).toBeInTheDocument();
    });

    const archSummary = screen.getByLabelText("Filter by architecture");
    fireEvent.click(archSummary);

    const archDetails = archSummary.closest("details");
    expect(archDetails?.open).toBe(true);

    fireEvent.pointerDown(screen.getByLabelText("Architecture nextgen"));

    expect(archDetails?.open).toBe(true);
  });

  it("shows colored section badges for nextgen region when Vector component is selected", async () => {
    render(<RegionsPage />);

    await waitFor(() => {
      expect(screen.getByText("dev-1")).toBeInTheDocument();
    });

    fireEvent.click(screen.getByLabelText("Filter by component"));
    fireEvent.click(screen.getByRole("button", { name: "Select component Vector" }));

    const dev1Row = screen.getByText("dev-1").closest("tr")!;
    // Section badges with agent-style colors
    const k8sBadge = within(dev1Row).getByText("k8s");
    expect(k8sBadge).toHaveClass("bg-blue-500/10", "text-blue-400", "border-blue-500/30");
    expect(dev1Row.textContent).toContain("v1-k8s");

    const spBadge = within(dev1Row).getByText("sharedpool");
    expect(spBadge).toHaveClass("bg-rose-500/10", "text-rose-400", "border-rose-500/30");
    expect(dev1Row.textContent).toContain("v1-sp");

    const tgBadge = within(dev1Row).getByText("tidbgroup");
    expect(tgBadge).toHaveClass("bg-cyan-500/10", "text-cyan-400", "border-cyan-500/30");
    expect(dev1Row.textContent).toContain("v1-tg");

    expect(dev1Row.textContent).not.toContain("tidbcluster");
  });

  it("shows k8s and tc badges for dedicated region", async () => {
    render(<RegionsPage />);

    await waitFor(() => {
      expect(screen.getByText("staging-1")).toBeInTheDocument();
    });

    fireEvent.click(screen.getByLabelText("Filter by component"));
    fireEvent.click(screen.getByRole("button", { name: "Select component Vector" }));

    const stagingRow = screen.getByText("staging-1").closest("tr")!;
    const k8sBadge = within(stagingRow).getByText("k8s");
    expect(k8sBadge).toHaveClass("bg-blue-500/10");

    const tcBadge = within(stagingRow).getByText("tidbcluster");
    expect(tcBadge).toHaveClass("bg-purple-500/10", "text-purple-400", "border-purple-500/30");

    expect(stagingRow.textContent).toContain("v2-k8s");
    expect(stagingRow.textContent).toContain("v2-tc");
    expect(stagingRow.textContent).not.toContain("sp");
    expect(stagingRow.textContent).not.toContain("tg");
  });

  it("shows single tag when ACS component is selected", async () => {
    render(<RegionsPage />);

    await waitFor(() => {
      expect(screen.getByText("dev-1")).toBeInTheDocument();
    });

    const dev1Row = screen.getByText("dev-1").closest("tr")!;
    expect(within(dev1Row).getByText("sha1")).toBeInTheDocument();
    expect(dev1Row.textContent).not.toContain("k8s:");
  });

  it("shows colored section badges for VMagent component", async () => {
    render(<RegionsPage />);

    await waitFor(() => {
      expect(screen.getByText("dev-1")).toBeInTheDocument();
    });

    fireEvent.click(screen.getByLabelText("Filter by component"));
    fireEvent.click(screen.getByRole("button", { name: "Select component VMagent" }));

    const dev1Row = screen.getByText("dev-1").closest("tr")!;
    const k8sBadge = within(dev1Row).getByText("k8s");
    expect(k8sBadge).toHaveClass("bg-blue-500/10");

    const spBadge = within(dev1Row).getByText("sharedpool");
    expect(spBadge).toHaveClass("bg-rose-500/10");

    expect(dev1Row.textContent).toContain("m1-k8s");
    expect(dev1Row.textContent).toContain("m1-sp");
  });

  it("shows multiple section badges side by side when consecutive tags are the same", async () => {
    mockRegionsApi({
      ok: true,
      data: {
        regions: [
          {
            id: "r-merged",
            architecture: "nextgen",
            environment: "dev",
            provider: "aws",
            regionName: "merged-1",
            directoryPath: "envs/merged-1",
            currentTag: "sha1",
            imageRegistry: "registry",
            componentTags: { acs: "sha1", vector: "v-same", vmagent: "m1" },
            sectionComponentTags: {
              k8s: { acs: null, vector: "v-same", vmagent: null },
              tidbcluster: { acs: null, vector: null, vmagent: null },
              sharedpool: { acs: null, vector: "v-same", vmagent: null },
              tidbgroup: { acs: null, vector: "v-diff", vmagent: null },
            },
            syncStatus: "enabled",
          },
        ],
      },
    });

    render(<RegionsPage />);

    await waitFor(() => {
      expect(screen.getByText("merged-1")).toBeInTheDocument();
    });

    fireEvent.click(screen.getByLabelText("Filter by component"));
    fireEvent.click(screen.getByRole("button", { name: "Select component Vector" }));

    const row = screen.getByText("merged-1").closest("tr")!;
    // k8s and sp share same tag → both badges appear, then : tag
    expect(row.textContent).toContain("v-same");
    expect(row.textContent).toContain("v-diff");

    const k8sBadge = within(row).getByText("k8s");
    const spBadge = within(row).getByText("sharedpool");
    const tgBadge = within(row).getByText("tidbgroup");
    expect(k8sBadge).toHaveClass("bg-blue-500/10");
    expect(spBadge).toHaveClass("bg-rose-500/10");
    expect(tgBadge).toHaveClass("bg-cyan-500/10");
  });

});
