import { expect, test } from "@playwright/test";

test.describe("User Management", () => {
  test.beforeEach(async ({ page }) => {
    await page.addInitScript(() => {
      const authority = "http://localhost:5000/oidc";
      const client_id = "adminfront";
      const key = `oidc.user:${authority}:${client_id}`;
      const authData = {
        id_token: "fake-id-token",
        access_token: "fake-token",
        token_type: "Bearer",
        scope: "openid profile email",
        profile: {
          sub: "admin-user",
          name: "Admin",
          email: "admin@test.com",
          role: "super_admin",
        },
        expires_at: Math.floor(Date.now() / 1000) + 36000,
      };
      window.localStorage.setItem(key, JSON.stringify(authData));
      window.localStorage.setItem("admin_session", "fake-token");
      window.localStorage.setItem("locale", "ko");
      window.localStorage.setItem("oidc.state", "dummy");

      (
        window as Window & typeof globalThis & { _IS_TEST_MODE?: boolean }
      )._IS_TEST_MODE = true;
    });

    await page.route("**/oidc/**", async (route) => {
      if (route.request().url().includes("/.well-known/openid-configuration")) {
        return route.fulfill({
          json: {
            issuer: "http://localhost:5000/oidc",
            authorization_endpoint: "http://localhost:5000/oidc/auth",
            token_endpoint: "http://localhost:5000/oidc/token",
            userinfo_endpoint: "http://localhost:5000/oidc/userinfo",
            jwks_uri: "http://localhost:5000/oidc/jwks",
          },
        });
      }
      await route.fulfill({ json: { issuer: "http://localhost:5000/oidc" } });
    });

    await page.route(/.*\/api\/v1\/.*/, async (route) => {
      const url = route.request().url();
      const method = route.request().method();

      if (url.includes("/user/me")) {
        return route.fulfill({
          json: {
            id: "admin-user",
            name: "Admin",
            email: "admin@test.com",
            role: "super_admin",
            manageableTenants: [],
          },
        });
      }

      if (url.match(/\/admin\/tenants(\?.*)?$/) && method === "GET") {
        return route.fulfill({
          json: {
            items: [
              {
                id: "t-1",
                slug: "test-tenant",
                name: "Test Tenant",
                config: {
                  userSchema: [
                    {
                      key: "loginId",
                      label: "Login ID",
                      type: "text",
                      isLoginId: true,
                    },
                  ],
                },
              },
              {
                id: "03dbe16b-e47b-4f72-927b-782807d67a35",
                slug: "tech-planning",
                name: "기술기획",
                type: "USER_GROUP",
                parentId: "hanmac-company-id",
                config: {},
              },
              {
                id: "hanmac-family-id",
                slug: "hanmac-family",
                name: "한맥가족",
                type: "COMPANY_GROUP",
                config: {},
              },
              {
                id: "hanmac-company-id",
                slug: "hanmac-company",
                name: "한맥기술",
                type: "COMPANY",
                parentId: "hanmac-family-id",
                config: {},
              },
              {
                id: "hanmac-team-id",
                slug: "hanmac-team",
                name: "한맥팀",
                type: "USER_GROUP",
                parentId: "hanmac-company-id",
                config: {},
              },
              {
                id: "system-id",
                slug: "system",
                name: "System Tenant",
                type: "SYSTEM",
                config: {},
              },
              {
                id: "external-tenant-id",
                slug: "external-tenant",
                name: "External Tenant",
                type: "COMPANY",
                config: {},
              },
            ],
            total: 7,
            limit: 100,
            offset: 0,
          },
        });
      }

      if (url.match(/\/admin\/tenants$/) && method === "POST") {
        const postData = route.request().postDataJSON();
        return route.fulfill({
          status: 201,
          json: {
            id: "personal-tenant-id",
            slug: postData?.slug || "personal",
            name: postData?.name || "Personal",
            type: postData?.type || "PERSONAL",
            config: {},
          },
        });
      }

      if (url.match(/\/admin\/tenants\/t-1$/) && method === "GET") {
        return route.fulfill({
          json: {
            id: "t-1",
            slug: "test-tenant",
            name: "Test Tenant",
            config: {
              userSchema: [
                {
                  key: "loginId",
                  label: "Login ID",
                  type: "text",
                  isLoginId: true,
                },
              ],
            },
          },
        });
      }

      if (
        url.match(/\/admin\/tenants\/03dbe16b-e47b-4f72-927b-782807d67a35$/) &&
        method === "GET"
      ) {
        return route.fulfill({
          json: {
            id: "03dbe16b-e47b-4f72-927b-782807d67a35",
            slug: "tech-planning",
            name: "기술기획",
            type: "USER_GROUP",
            config: {},
          },
        });
      }

      if (url.match(/\/admin\/users\/u-1$/) && method === "GET") {
        return route.fulfill({
          json: {
            id: "u-1",
            name: "John Doe",
            email: "john@test.com",
            loginId: "johndoe",
            tenantSlug: "test-tenant",
            tenant: { id: "t-1", name: "Test Tenant", slug: "test-tenant" },
            role: "user",
            status: "active",
            metadata: { "t-1": { loginId: "johndoe" } },
          },
        });
      }

      if (url.includes("/password/policy")) {
        return route.fulfill({
          json: {
            minLength: 12,
            lowercase: true,
            uppercase: true,
            number: true,
            nonAlphanumeric: true,
          },
        });
      }

      if (url.includes("/rp-history")) {
        return route.fulfill({
          json: [],
        });
      }

      if (url.match(/\/admin\/users(\?.*)?$/) && method === "POST") {
        // Parse request payload to simulate validation checks
        const postData = route.request().postDataJSON();
        if (postData && postData.metadata?.loginId === "existing_user") {
          // Simulate a backend conflict error (409) for an existing loginId
          return route.fulfill({
            status: 409,
            json: {
              error: "이미 존재하는 로그인 ID 입니다.",
            },
          });
        }
        // Mock successful user creation
        return route.fulfill({
          json: {
            id: "new-user-id",
            name: "New User",
            email: "newuser@test.com",
            loginId: postData?.metadata?.loginId || "newuser123",
          },
        });
      }

      if (url.match(/\/admin\/users\/u-1$/) && method === "PUT") {
        const postData = route.request().postData();
        console.log("PUT /admin/users/u-1 payload:", postData);

        // Force 409 error for this specific conflict string
        if (postData?.includes("johndoe_conflict")) {
          return route.fulfill({
            status: 409,
            json: {
              error: "이미 존재하는 로그인 ID 입니다.",
            },
          });
        }

        // Mock successful user update
        return route.fulfill({
          json: {
            id: "u-1",
            name: "John Doe Updated",
            email: "john@test.com",
            loginId: "johndoe_updated",
            status: "inactive",
          },
        });
      }

      if (url.match(/\/admin\/users(\?.*)?$/) && method === "GET") {
        return route.fulfill({
          json: {
            items: [
              {
                id: "u-1",
                name: "John Doe",
                email: "john@test.com",
                phone: "010-1111-2222",
                loginId: "johndoe",
                role: "user",
                status: "active",
                createdAt: "2026-04-01T00:00:00Z",
              },
            ],
            total: 1,
            limit: 50,
            offset: 0,
          },
        });
      }

      return route.fulfill({ json: { items: [], total: 0 } });
    });
  });

  test("should successfully edit a user's Login ID", async ({ page }) => {
    await page.goto("/users/u-1");

    // "테넌트 프로필" 탭 클릭
    await page
      .getByRole("tab", { name: /테넌트 프로필|Tenants|Tenant Profile/i })
      .click();

    // Wait for the form to load with the existing login ID
    const loginIdInput = page.locator(
      'input[name*="metadata"][name*="loginId"]',
    );
    await expect(loginIdInput).toBeVisible();
    await expect(loginIdInput).toHaveValue("johndoe");

    // Change the Login ID
    await loginIdInput.fill("johndoe_updated");

    // Submit the form using Enter key
    await loginIdInput.press("Enter");

    // Check for success message
    await expect(page.getByText(/저장/i).first()).toBeVisible();
  });

  test("should manage global custom claim permissions in user detail", async ({
    page,
  }) => {
    let updatePayload: Record<string, unknown> | undefined;

    await page.route(/\/admin\/users\/u-1$/, async (route) => {
      const method = route.request().method();

      if (method === "GET") {
        return route.fulfill({
          json: {
            id: "u-1",
            name: "John Doe",
            email: "john@test.com",
            loginId: "johndoe",
            tenantSlug: "test-tenant",
            tenant: { id: "t-1", name: "Test Tenant", slug: "test-tenant" },
            role: "user",
            status: "active",
            metadata: {
              "t-1": { loginId: "johndoe" },
              global_custom_claims: {
                contract_date: "2026-06-09",
              },
              global_custom_claim_types: {
                contract_date: "date",
              },
              global_custom_claim_permissions: {
                contract_date: {
                  readPermission: "user_and_admin",
                  writePermission: "admin_only",
                },
              },
            },
          },
        });
      }

      if (method === "PUT") {
        updatePayload = route.request().postDataJSON();
        return route.fulfill({
          json: {
            id: "u-1",
            name: "John Doe",
            email: "john@test.com",
            loginId: "johndoe",
            status: "active",
            metadata: updatePayload?.metadata,
          },
        });
      }

      return route.fallback();
    });

    await page.goto("/users/u-1");
    await page
      .getByRole("tab", { name: /전역 Custom Claims|Custom Claims/i })
      .click();

    await expect(
      page.getByTestId("global-custom-claim-key-contract_date"),
    ).toHaveValue("contract_date");
    await expect(
      page.getByTestId("global-custom-claim-read-permission-contract_date"),
    ).toHaveValue("user_and_admin");
    await expect(
      page.getByTestId("global-custom-claim-write-permission-contract_date"),
    ).toHaveValue("admin_only");

    await page
      .getByTestId("global-custom-claim-write-permission-contract_date")
      .selectOption("user_and_admin");

    await page.screenshot({
      path: "test-results/adminfront-global-custom-claim-permissions.png",
      fullPage: true,
    });

    await page
      .getByRole("button", { name: /전역 Claim 저장|Save Global Claim/i })
      .click();

    await expect
      .poll(() => updatePayload)
      .toMatchObject({
        metadata: {
          global_custom_claims: {
            contract_date: "2026-06-09",
          },
          global_custom_claim_types: {
            contract_date: "date",
          },
          global_custom_claim_permissions: {
            contract_date: {
              readPermission: "user_and_admin",
              writePermission: "user_and_admin",
            },
          },
        },
      });
  });

  test("should configure global custom claim definitions", async ({ page }) => {
    let updatePayload:
      | {
          items?: Array<Record<string, unknown>>;
        }
      | undefined;

    await page.route(/\/admin\/global-custom-claims$/, async (route) => {
      const method = route.request().method();

      if (method === "GET") {
        return route.fulfill({
          json: {
            items: [
              {
                key: "contract_date",
                label: "Contract date",
                valueType: "date",
                readPermission: "user_and_admin",
                writePermission: "admin_only",
                description: "전체 RP에 공통 제공되는 계약일",
              },
            ],
          },
        });
      }

      if (method === "PUT") {
        updatePayload = route.request().postDataJSON();
        return route.fulfill({ json: updatePayload });
      }

      return route.fallback();
    });

    await page.goto("/users/custom-claims");

    await expect(page.getByText("전역 Claim 설정")).toBeVisible();
    await expect(
      page.getByTestId("global-claim-definition-key-contract_date"),
    ).toHaveValue("contract_date");
    await expect(
      page.getByTestId("global-claim-definition-read-permission-contract_date"),
    ).toHaveValue("user_and_admin");
    await expect(
      page.getByTestId(
        "global-claim-definition-write-permission-contract_date",
      ),
    ).toHaveValue("admin_only");

    await page
      .getByTestId("global-claim-definition-write-permission-contract_date")
      .selectOption("user_and_admin");

    await page.screenshot({
      path: "test-results/adminfront-global-custom-claim-definition-settings.png",
      fullPage: true,
    });

    await page.getByRole("button", { name: /^저장$|^Save$/i }).click();

    await expect
      .poll(() => updatePayload)
      .toMatchObject({
        items: [
          {
            key: "contract_date",
            label: "Contract date",
            valueType: "date",
            readPermission: "user_and_admin",
            writePermission: "user_and_admin",
          },
        ],
      });
  });

  test("should show conflict error when updating to an existing Login ID", async ({
    page,
  }) => {
    // Intercept ANY PUT request to this user and return 409
    await page.route(/\/admin\/users\/u-1/, async (route) => {
      if (route.request().method() === "PUT") {
        return route.fulfill({
          status: 409,
          contentType: "application/json",
          body: JSON.stringify({ error: "이미 존재하는 로그인 ID 입니다." }),
        });
      }
      return route.fallback();
    });

    await page.goto("/users/u-1");

    // "테넌트 프로필" 탭 클릭
    await page
      .getByRole("tab", { name: /테넌트 프로필|Tenants|Tenant Profile/i })
      .click();

    const loginIdInput = page.locator(
      'input[name*="metadata"][name*="loginId"]',
    );
    await expect(loginIdInput).toBeVisible();
    await expect(loginIdInput).toHaveValue("johndoe");

    // Use a value similar to the successful edit test
    await loginIdInput.fill("johndoe_conflict");

    // Submit the form using Enter key
    await loginIdInput.press("Enter");

    // Check for the specific error
    await expect(
      page.getByText(/이미 존재하는 로그인 ID 입니다/i).first(),
    ).toBeVisible();
  });

  test("should successfully create a new user with a Login ID", async ({
    page,
  }) => {
    await page.goto("/users/new");

    // Ensure the page title is loaded
    await expect(page.getByText(/사용자 추가/i).first()).toBeVisible();
    const categoryTabs = page.getByRole("tab");
    await expect(categoryTabs).toHaveText([
      "한맥가족 구성원",
      "외부 기업 회원",
      "개인 회원",
    ]);
    await expect(
      page.getByRole("tab", { name: /외부 기업 회원/i }),
    ).toBeVisible();
    await expect(
      page.getByRole("tab", { name: /한맥가족 구성원/i }),
    ).toBeVisible();
    await expect(page.getByRole("tab", { name: /개인 회원/i })).toBeVisible();
    await expect(
      page.getByRole("tab", { name: /한맥가족 구성원/i }),
    ).toHaveAttribute("data-state", "active");
    await expect(page.getByLabel(/한맥 가족 구성원으로 등록/i)).toHaveCount(0);

    // Select Tenant first (important for schema fields to show up)
    await page.getByRole("tab", { name: /외부 기업 회원/i }).click();
    await page.selectOption("select#tenantSlug", "test-tenant");

    // Fill required fields
    await page.locator('input[name="name"]').fill("New User");
    await page.locator('input[name="email"]').fill("newuser@test.com");

    // Fill Login ID
    const loginIdInput = page.locator('input[id*="metadata"][id*="loginId"]');
    await loginIdInput.fill("newuser123");

    // Submit the form
    const createButton = page.getByRole("button", { name: /생성/i });
    await createButton.click();

    // Assuming successful creation redirects back to the user list
    await expect(page).toHaveURL(/.*\/users$/, { timeout: 10000 });
  });

  test("should export users through the authenticated API client", async ({
    page,
  }) => {
    let authorizationHeader: string | undefined;
    let exportUrl = "";

    await page.route(/\/admin\/users\/export(\?.*)?$/, async (route) => {
      authorizationHeader = route.request().headers().authorization;
      exportUrl = route.request().url();
      return route.fulfill({
        status: 200,
        headers: {
          "content-type": "text/csv; charset=utf-8",
          "content-disposition": 'attachment; filename="users.csv"',
        },
        body: "email,name\njohn@test.com,John Doe\n",
      });
    });

    await page.goto("/users");

    const [download] = await Promise.all([
      page.waitForEvent("download"),
      (async () => {
        await page.getByTestId("user-data-mgmt-btn").click();
        await page.getByTestId("user-export-menu-item").click();
      })(),
    ]);

    expect(download.suggestedFilename()).toBe("users.csv");
    expect(authorizationHeader).toBe("Bearer fake-token");
    expect(exportUrl).toContain("includeIds=false");
  });

  test("should show contact info in one row, hide roles, and change user status", async ({
    page,
  }) => {
    let updatePayload: Record<string, unknown> | undefined;

    await page.route(/\/admin\/users\/u-1$/, async (route) => {
      if (route.request().method() === "PUT") {
        updatePayload = route.request().postDataJSON();
        return route.fulfill({
          json: {
            id: "u-1",
            name: "John Doe",
            email: "john@test.com",
            phone: "010-1111-2222",
            loginId: "johndoe",
            status: "preboarding",
            createdAt: "2026-04-01T00:00:00Z",
          },
        });
      }
      return route.fallback();
    });

    await page.goto("/users");
    const table = page.locator("table");
    await expect(
      table.getByRole("columnheader", { name: /ROLE|역할/i }),
    ).toBeVisible();

    await page.getByTestId("user-status-select-u-1").click();
    await page.getByRole("option", { name: /입사대기|Preboarding/ }).click();
    await expect
      .poll(() => updatePayload)
      .toMatchObject({ status: "preboarding" });
  });

  test("should center users table loading state and use compact headers", async ({
    page,
  }) => {
    let resolveUsers: (() => void) | undefined;
    const usersGate = new Promise<void>((resolve) => {
      resolveUsers = resolve;
    });

    await page.route(/\/admin\/users(\?.*)?$/, async (route) => {
      if (route.request().method() !== "GET") {
        return route.fallback();
      }

      await usersGate;
      return route.fulfill({
        json: {
          items: [],
          total: 0,
          limit: 50,
          offset: 0,
        },
      });
    });

    await page.goto("/users");

    const loadingCell = page.getByTestId("user-table-loading-cell");
    await expect(loadingCell).toBeVisible();
    await expect(loadingCell).toHaveCSS("display", "flex");
    await expect(loadingCell).toHaveCSS("align-items", "center");
    await expect(loadingCell).toHaveCSS("justify-content", "center");

    const nameHeader = page.getByRole("columnheader", { name: /이름|Name/i });
    await expect(nameHeader).toHaveClass(/h-9/);
    await expect(nameHeader.locator("> div")).toHaveClass(/h-full/);

    resolveUsers?.();
    await expect(page.getByTestId("user-table-empty-cell")).toBeVisible();
  });

  test("should virtualize large user result rows in the users table", async ({
    page,
  }) => {
    const manyUsers = Array.from({ length: 500 }, (_, index) => ({
      id: `u-${index}`,
      name: `User ${index}`,
      email: `user${index}@test.com`,
      phone: "010-1111-2222",
      loginId: `user${index}`,
      role: "user",
      status: "active",
      createdAt: "2026-04-01T00:00:00Z",
    }));

    await page.route(/\/admin\/users(\?.*)?$/, async (route) => {
      if (route.request().method() !== "GET") {
        return route.fallback();
      }
      return route.fulfill({
        json: {
          items: manyUsers,
          total: manyUsers.length,
          limit: manyUsers.length,
          offset: 0,
        },
      });
    });

    await page.goto("/users");
    await expect(page.getByText("User 0")).toBeVisible();

    const renderedStatusControls = await page
      .getByTestId(/^user-status-select-/)
      .count();
    expect(renderedStatusControls).toBeLessThan(manyUsers.length);
    await expect(page.getByText("User 499")).toHaveCount(0);

    await page.getByTestId("user-table-viewport").evaluate((element) => {
      element.scrollTop = element.scrollHeight;
      element.dispatchEvent(new Event("scroll", { bubbles: true }));
    });

    await expect(page.getByText("User 499")).toBeVisible();
  });

  test("should keep large user search rendering under 200ms", async ({
    page,
  }) => {
    const manyUsers = Array.from({ length: 20_000 }, (_, index) => ({
      id: `load-u-${index}`,
      name: `Load User ${index}`,
      email: `load-user-${index}@test.com`,
      phone: "010-1111-2222",
      loginId: `load-user-${index}`,
      role: "user",
      status: "active",
      createdAt: "2026-04-01T00:00:00Z",
    }));

    await page.route(/\/admin\/users(\?.*)?$/, async (route) => {
      if (route.request().method() !== "GET") {
        return route.fallback();
      }

      const url = new URL(route.request().url());
      const normalizedSearch = url.searchParams
        .get("search")
        ?.trim()
        .toLowerCase();
      const items = normalizedSearch
        ? manyUsers.filter((user) =>
            `${user.name} ${user.email}`
              .toLowerCase()
              .includes(normalizedSearch),
          )
        : manyUsers;

      return route.fulfill({
        json: {
          items,
          total: items.length,
          limit: items.length,
          offset: 0,
        },
      });
    });

    const initialStartedAt = performance.now();
    await page.goto("/users");
    await expect(page.getByText("Load User 0")).toBeVisible();
    const initialMs = performance.now() - initialStartedAt;

    const searchInput = page.getByPlaceholder("이름 또는 이메일 검색");
    await searchInput.fill("Load User 19999");
    const searchMs = await page.evaluate(async () => {
      const input = Array.from(document.querySelectorAll("input")).find(
        (candidate) => candidate.placeholder === "이름 또는 이메일 검색",
      );

      if (!input) {
        throw new Error("User search input was not found.");
      }

      return await new Promise<number>((resolve, reject) => {
        const startedAt = performance.now();
        const timeout = window.setTimeout(() => {
          observer.disconnect();
          reject(new Error("Timed out waiting for large user search result."));
        }, 1000);
        const observer = new MutationObserver(() => {
          const bodyText = document.body.textContent ?? "";
          if (
            bodyText.includes("Load User 19999") &&
            !bodyText.includes("Load User 0")
          ) {
            window.clearTimeout(timeout);
            observer.disconnect();
            resolve(performance.now() - startedAt);
          }
        });

        observer.observe(document.body, {
          childList: true,
          characterData: true,
          subtree: true,
        });
        input.dispatchEvent(
          new KeyboardEvent("keydown", {
            bubbles: true,
            cancelable: true,
            key: "Enter",
          }),
        );
      });
    });
    await expect(page.getByText("Load User 19999")).toBeVisible();
    await expect(page.getByText("Load User 0")).toHaveCount(0);

    console.log(
      `[perf] users initial render with ${manyUsers.length} rows: ${initialMs.toFixed(1)}ms`,
    );
    console.log(
      `[perf] users search update with ${manyUsers.length} rows: ${searchMs.toFixed(1)}ms`,
    );
    expect(searchMs).toBeLessThan(500);
  });

  test("should expose internal user uuid in the users table", async ({
    page,
  }) => {
    const internalUserId = "4d20c735-05d0-42d4-9479-0e9be74fd987";

    await page.route(/\/admin\/users(\?.*)?$/, async (route) => {
      if (route.request().method() !== "GET") {
        return route.fallback();
      }
      return route.fulfill({
        json: {
          items: [
            {
              id: internalUserId,
              name: "UUID User",
              email: "uuid-user@test.com",
              phone: "010-2222-3333",
              loginId: "uuid_login_id",
              role: "user",
              status: "active",
              createdAt: "2026-04-01T00:00:00Z",
            },
          ],
          total: 1,
          limit: 50,
          offset: 0,
        },
      });
    });

    await page.goto("/users");
    await expect(page.locator("table")).toContainText(internalUserId);
  });

  test("should require a tenant appointment before creating a Hanmac family user", async ({
    page,
  }) => {
    let createPayload: Record<string, unknown> | undefined;

    await page.route(/\/admin\/users(\?.*)?$/, async (route) => {
      if (route.request().method() === "POST") {
        createPayload = route.request().postDataJSON();
        return route.fulfill({
          status: 201,
          json: {
            id: "new-user-id",
            name: "Family User",
            email: "family@test.com",
          },
        });
      }
      return route.fallback();
    });

    await page.goto("/users/new");

    await expect(
      page.getByRole("tab", { name: /한맥가족 구성원/i }),
    ).toHaveAttribute("data-state", "active");
    await expect(page.getByLabel(/한맥 가족 구성원으로 등록/i)).toHaveCount(0);
    await expect(page.locator("select#role")).toBeVisible();
    await expect(page.locator("input#department")).toHaveCount(0);

    await expect(page.getByText(/대표 소속/i)).toHaveCount(0);
    await page.getByTestId("add-appointment-btn").click();
    await expect(page.getByTestId("appointment-row-0")).toBeVisible();
    await expect(
      page.getByTestId("appointment-tenant-owner-line-0"),
    ).toBeVisible();
    await expect(page.getByTestId("appointment-position-line-0")).toBeVisible();
    await page.getByRole("switch", { name: /대표 조직/i }).click();
    await page.getByLabel(/^직무$/i).fill("플랫폼 운영");
    await page.getByLabel(/^직책$/i).fill("팀장");

    await page.locator('input[name="name"]').fill("Family User");
    await page.locator('input[name="email"]').fill("family@test.com");
    await page.getByRole("button", { name: /생성/i }).click();

    await expect(
      page.getByText(
        /한맥 가족 구성원은 소속 테넌트를 하나 이상 선택해 주세요\./,
      ),
    ).toBeVisible();
    expect(createPayload).toBeUndefined();
  });

  test("should hide Hanmac family subtree and system tenants when creating a non-family user", async ({
    page,
  }) => {
    await page.goto("/users/new");

    await page.getByRole("tab", { name: /외부 기업 회원/i }).click();

    const tenantOptionValues = await page
      .locator("select#tenantSlug option")
      .evaluateAll((options) =>
        options.map((option) => (option as HTMLOptionElement).value),
      );

    expect(tenantOptionValues).toContain("test-tenant");
    expect(tenantOptionValues).toContain("external-tenant");
    expect(tenantOptionValues).not.toContain("");
    expect(tenantOptionValues).not.toContain("system");
    expect(tenantOptionValues).not.toContain("hanmac-family");
    expect(tenantOptionValues).not.toContain("hanmac-company");
    expect(tenantOptionValues).not.toContain("hanmac-team");
    expect(tenantOptionValues).not.toContain("tech-planning");
  });

  test("should create a personal user with the fixed global Personal tenant when it is not listed", async ({
    page,
  }) => {
    let tenantPayload: Record<string, unknown> | undefined;
    let createPayload: Record<string, unknown> | undefined;

    await page.route(/\/admin\/tenants$/, async (route) => {
      if (route.request().method() === "POST") {
        tenantPayload = route.request().postDataJSON();
        return route.fulfill({
          status: 201,
          json: {
            id: "personal-tenant-id",
            slug: "personal",
            name: "Personal",
            type: "PERSONAL",
            config: {},
          },
        });
      }
      return route.fallback();
    });

    await page.route(/\/admin\/users(\?.*)?$/, async (route) => {
      if (route.request().method() === "POST") {
        createPayload = route.request().postDataJSON();
        return route.fulfill({
          status: 201,
          json: {
            id: "personal-user-id",
            name: "Personal User",
            email: "personal@test.com",
          },
        });
      }
      return route.fallback();
    });

    await page.goto("/users/new");
    await page.getByRole("tab", { name: /개인 회원/i }).click();

    await expect(page.getByTestId("personal-tenant-summary")).toContainText(
      /Personal/i,
    );
    await page.locator('input[name="name"]').fill("Personal User");
    await page.locator('input[name="email"]').fill("personal@test.com");
    await page.getByRole("button", { name: /생성/i }).click();

    expect(tenantPayload).toBeUndefined();
    await expect
      .poll(() => createPayload)
      .toMatchObject({
        tenantSlug: "personal",
        metadata: {
          personalTenantId: "9607eb7b-04d2-42ab-80fe-780fe21c7e8f",
        },
      });
  });

  test("should show Hanmac family appointments layout on user detail", async ({
    page,
  }) => {
    await page.setViewportSize({ width: 520, height: 900 });
    await page.route(/\/admin\/users\/u-1$/, async (route) => {
      if (route.request().method() === "GET") {
        return route.fulfill({
          json: {
            id: "u-1",
            name: "Family User",
            email: "family@test.com",
            phone: "010-1111-2222",
            loginId: "familyuser",
            role: "user",
            status: "active",
            createdAt: "2026-04-01T00:00:00Z",
            updatedAt: "2026-04-01T00:00:00Z",
            metadata: {
              additionalAppointments: [
                {
                  tenantId: "03dbe16b-e47b-4f72-927b-782807d67a35",
                  tenantSlug: "tech-planning",
                  tenantName: "기술기획 장기 운영 전략 조직",
                  isPrimary: true,
                  isOwner: true,
                  grade: "책임",
                  jobTitle: "플랫폼 운영",
                  position: "팀장",
                },
              ],
            },
          },
        });
      }
      return route.fallback();
    });

    await page.goto("/users/u-1");

    await expect(
      page.getByRole("tab", { name: /한맥가족 구성원/i }),
    ).toHaveAttribute("data-state", "active");
    await expect(page.getByLabel(/한맥 가족 구성원으로 등록/i)).toHaveCount(0);
    await expect(page.getByTestId("detail-appointment-row-0")).toBeVisible();
    await expect(
      page.getByTestId("detail-appointment-tenant-owner-line-0"),
    ).toContainText(/기술기획|대표 조직|조직장/);
    await expect(
      page.getByTestId("detail-appointment-tenant-owner-controls-0"),
    ).toHaveCSS("flex-wrap", "nowrap");

    const tenantPickerBox = await page
      .getByTestId("detail-appointment-tenant-picker-0")
      .boundingBox();
    const ownerSwitchBox = await page
      .getByTestId("detail-appointment-row-0")
      .getByRole("switch", { name: /대표 조직/i })
      .boundingBox();
    const managerSwitchBox = await page
      .getByTestId("detail-appointment-row-0")
      .getByRole("switch", { name: /조직장/i })
      .boundingBox();

    if (!tenantPickerBox || !ownerSwitchBox || !managerSwitchBox) {
      throw new Error("Appointment tenant owner controls are not visible.");
    }

    const centerYs = [
      tenantPickerBox.y + tenantPickerBox.height / 2,
      ownerSwitchBox.y + ownerSwitchBox.height / 2,
      managerSwitchBox.y + managerSwitchBox.height / 2,
    ];
    expect(Math.max(...centerYs) - Math.min(...centerYs)).toBeLessThan(8);
    await expect(
      page.getByTestId("detail-appointment-row-0").getByRole("switch", {
        name: /대표 조직/i,
      }),
    ).toBeChecked();
    await expect(
      page.getByTestId("detail-appointment-row-0").getByRole("switch", {
        name: /대표 조직/i,
      }),
    ).toBeDisabled();
    await expect(
      page.getByTestId("detail-appointment-position-line-0"),
    ).toBeVisible();
  });

  test("should save selected Hanmac representative appointment from user detail", async ({
    page,
  }) => {
    let updatePayload: Record<string, unknown> | undefined;

    await page.route(/\/admin\/users\/u-1$/, async (route) => {
      if (route.request().method() === "GET") {
        return route.fulfill({
          json: {
            id: "u-1",
            name: "Family User",
            email: "family@test.com",
            phone: "010-1111-2222",
            loginId: "familyuser",
            role: "user",
            status: "active",
            createdAt: "2026-04-01T00:00:00Z",
            updatedAt: "2026-04-01T00:00:00Z",
            metadata: {
              additionalAppointments: [
                {
                  tenantId: "03dbe16b-e47b-4f72-927b-782807d67a35",
                  tenantSlug: "tech-planning",
                  tenantName: "기술기획",
                  isOwner: true,
                  grade: "책임",
                  jobTitle: "플랫폼 운영",
                  position: "팀장",
                },
                {
                  tenantId: "hanmac-team-id",
                  tenantSlug: "hanmac-team",
                  tenantName: "한맥팀",
                  isOwner: false,
                  grade: "선임",
                  jobTitle: "개발",
                  position: "파트장",
                },
              ],
            },
          },
        });
      }
      if (route.request().method() === "PUT") {
        updatePayload = route.request().postDataJSON();
        return route.fulfill({
          json: {
            id: "u-1",
            name: "Family User",
            email: "family@test.com",
            status: "active",
          },
        });
      }
      return route.fallback();
    });

    await page.goto("/users/u-1");
    await page
      .getByTestId("detail-appointment-row-1")
      .getByRole("switch", { name: /대표 조직/i })
      .click();
    await expect(
      page.getByTestId("detail-appointment-row-0").getByRole("switch", {
        name: /대표 조직/i,
      }),
    ).not.toBeChecked();
    await expect(
      page.getByTestId("detail-appointment-row-1").getByRole("switch", {
        name: /대표 조직/i,
      }),
    ).toBeChecked();
    await page.locator("form").evaluate((form) => {
      (form as HTMLFormElement).requestSubmit();
    });

    await expect
      .poll(() => updatePayload)
      .toMatchObject({
        tenantSlug: "hanmac-team",
        primaryTenantId: "hanmac-team-id",
        primaryTenantName: "한맥팀",
        metadata: {
          primaryTenantId: "hanmac-team-id",
          primaryTenantName: "한맥팀",
          primaryTenantSlug: "hanmac-team",
          additionalAppointments: [
            {
              tenantId: "03dbe16b-e47b-4f72-927b-782807d67a35",
              isPrimary: false,
            },
            { tenantId: "hanmac-team-id", isPrimary: true },
          ],
        },
      });
    expect(updatePayload?.primaryTenantIsOwner).toBeUndefined();
    expect(
      (updatePayload?.metadata as Record<string, unknown>)
        ?.primaryTenantIsOwner,
    ).toBeUndefined();
    const appointments = (updatePayload?.metadata as Record<string, unknown>)
      ?.additionalAppointments as Array<Record<string, unknown>>;
    expect(appointments[1].isOwner).toBeUndefined();
  });

  test("should show conflict error when creating with an existing Login ID", async ({
    page,
  }) => {
    await page.goto("/users/new");

    await expect(page.getByText(/사용자 추가/i).first()).toBeVisible();

    await page.getByRole("tab", { name: /외부 기업 회원/i }).click();

    // Select Tenant first (important for schema fields to show up)
    await page.selectOption("select#tenantSlug", "test-tenant");

    // Fill required fields
    await page.locator('input[name="name"]').fill("New User");
    await page.locator('input[name="email"]').fill("newuser@test.com");

    // Fill Login ID that triggers the mock conflict error
    const loginIdInput = page.locator('input[id*="metadata"][id*="loginId"]');
    await loginIdInput.fill("existing_user");

    // Submit the form
    const createButton = page.getByRole("button", { name: /생성/i });
    await createButton.click();

    // Check for the specific conflict error message from the backend mock
    await expect(
      page.getByText(/이미 존재하는 로그인 ID 입니다/i),
    ).toBeVisible();
  });
});
