/**
 * UC22 — Case 250: **2 (hoặc nhiều) người cùng tạo mới thư mục tại cùng một thời điểm**
 * — **CÙNG TÊN thư mục**.
 *
 * Kịch bản (theo mô tả người dùng):
 *   1. Tạo 1 Bộ hồ sơ.
 *   2. Mở 2 trình duyệt, mỗi trình duyệt chuẩn bị **đầy đủ** dữ liệu tạo thư mục với **cùng một
 *      tên** (chưa lưu), sau đó **cả 2 bấm Xác nhận cùng lúc**.
 *   3. Mong muốn: **chỉ tạo được 1 thư mục**; trình duyệt còn lại báo
 *      **"Tên thư mục đã tồn tại. Vui lòng kiểm tra lại"**.
 *
 * Đây là case đối xứng của [`1.4.1.249.spec.ts`](./1.4.1.249.spec.ts) (2 người, **tên khác nhau**
 * → tạo được cả 2).
 *
 * 📌 Ghi chú cách viết (theo yêu cầu người dùng 2026-08-01):
 * - Toàn bộ bước dựng dữ liệu + thao tác viết **thẳng trong file spec này**, không đẩy vào
 *   `1.4.1.setup.ts` / `1.4.1.steps.ts`.
 * - "Cùng một thời điểm" = `Promise.all`: 2 lệnh click phát đi trong **cùng 1 tick** trên 2 context
 *   khác nhau, không chờ nhau. Không thể đảm bảo 2 request tới server trùng đúng mili-giây.
 * - **Không giả định trình duyệt nào thắng** — thứ tự tới server là ngẫu nhiên. Mỗi phiên tự "đua"
 *   giữa 2 tín hiệu (toast "Thành công" ⟷ thông báo trùng tên), sau đó case assert:
 *   **đúng 1 phiên thành công, các phiên còn lại báo trùng tên**.
 *
 * ⚠️ Chưa khảo sát (ghi ở `1.4.1.md` mục 4):
 * - **Vị trí hiển thị** thông báo "Tên thư mục đã tồn tại..." (toast `.ant-message-notice` hay lỗi
 *   inline `.ant-form-item-explain-error` trong modal) **chưa có trong tài liệu màn hình** → spec
 *   dò bằng `page.getByText(...)` để không phụ thuộc chỗ render. Khảo sát xong nên siết lại
 *   selector và bổ sung thông báo này vào `KHO-TAI-LIEU.MODAL-TAO-THU-MUC.md`.
 * - Người thứ hai (Admin `ecm09`) được cấp **Owner ở BHS** lúc tạo + BHS `chuyenHoatDong` — vai
 *   Admin chưa được khảo sát (`KHO-TAI-LIEU.PHAN-QUYEN-THEO-VAI.md` mục 5) nên không dựa vào giả
 *   định "Admin mặc định thấy mọi BHS".
 */
import { Locator, Page } from "@playwright/test";
import { test, expect, ACCOUNT } from "../../src/fixture/base-test";
import { TIMEOUT } from "../../src/constant/timeout";
import { PW } from "../../src/utils/PW";
import {
  DOC_PERM,
  THU_MUC_FIELD,
  createBoHoSo,
  chuyenHoatDong,
  openBoHoSo,
  openTabCauTrucHoSo,
  openModalThemThuMuc,
  dongModalThuMucNeuDangMo,
  pickFirstOption,
  getToast,
  getActiveTabPane,
  getCauTrucRow,
} from "../../src/screen-instructions/KHO-TAI-LIEU.functions";

/** Thông báo QA yêu cầu ở phiên **thua** (nguyên văn) */
const THONG_BAO_TRUNG_TEN = "Tên thư mục đã tồn tại. Vui lòng kiểm tra lại";
/** Từ khoá để **phát hiện** phiên nào bị chặn (khoan so nguyên văn — so ở bước assert) */
const TU_KHOA_TRUNG_TEN = "Tên thư mục đã tồn tại";

/** Kết quả của 1 phiên sau khi bấm "Xác nhận" */
type KetQuaLuu = "thanhCong" | "trungTen";

/** 1 người tham gia thao tác đồng thời */
type NguoiThamGia = {
  /** Fixture (phiên đăng nhập) của người này */
  page: Page;
  /** Account id — người **đầu tiên** là người tạo BHS, những người sau được cấp Owner ở BHS */
  account: string;
  /** Modal "Thêm thư mục" đã chuẩn bị xong dữ liệu, chờ bấm Xác nhận */
  modal?: Locator;
  /** Kết quả đo được sau khi bấm Xác nhận */
  ketQua?: KetQuaLuu;
};

/**
 * Mở modal "Thêm thư mục" ở gốc BHS và **điền đủ dữ liệu bắt buộc nhưng KHÔNG lưu**.
 *
 * Điền đúng những trường mà `createThuMuc` điền (Tên + Đơn vị soạn thảo — trường bắt buộc tuỳ cấu
 * hình Cấu trúc hồ sơ, xem `KHO-TAI-LIEU.MODAL-TAO-THU-MUC.md` mục 5); Độ mật / Trạng thái hiển thị
 * để mặc định kế thừa từ BHS. Cả 2 phiên dùng **cùng một `tenThuMuc`**.
 */
const chuanBiDuLieuTaoThuMuc = async (
  page: Page,
  recordUrl: string,
  tenThuMuc: string,
): Promise<Locator> => {
  const modal = await openModalThemThuMuc(page, recordUrl);

  await modal.getByTestId(THU_MUC_FIELD.TEN).fill(tenThuMuc);

  // Field metadata có thể bị ẩn tuỳ cấu hình Cấu trúc hồ sơ → luôn guard isVisible (mục 3a)
  const donViSoanThao = modal.getByTestId(THU_MUC_FIELD.DON_VI_SOAN_THAO);
  if (await donViSoanThao.isVisible().catch(() => false)) {
    await pickFirstOption(page, donViSoanThao);
  }

  await expect(modal.getByTestId(THU_MUC_FIELD.TEN), {
    message: `Lỗi: modal "Thêm thư mục" chưa nhận tên "${tenThuMuc}" trước khi bấm lưu`,
  }).toHaveValue(tenThuMuc, { timeout: TIMEOUT.VALIDATE_WAITING });

  return modal;
};

/**
 * Bấm "Xác nhận" rồi **đua 2 tín hiệu** để biết phiên này thắng hay bị chặn.
 *
 * ⚠️ Quan sát được bắt đầu **trước** khi click: toast chỉ sống vài giây
 * (`KHO-TAI-LIEU.md` mục 9.8), chờ click xong mới quan sát là có thể trượt mất.
 * ⚠️ Vị trí render thông báo trùng tên chưa khảo sát → dò bằng `getByText` (khớp theo chuỗi con,
 * bắt được cả toast lẫn lỗi inline trong modal).
 */
const bamLuuVaDoKetQua = async (
  page: Page,
  modal: Locator,
  account: string,
): Promise<KetQuaLuu> => {
  const toastThanhCong = getToast(page, "Thành công");
  const baoTrungTen = page.getByText(TU_KHOA_TRUNG_TEN).first();

  const dangDoi = Promise.race([
    toastThanhCong
      .waitFor({ state: "visible", timeout: TIMEOUT.PAGE_LOADING })
      .then((): KetQuaLuu => "thanhCong"),
    baoTrungTen
      .waitFor({ state: "visible", timeout: TIMEOUT.PAGE_LOADING })
      .then((): KetQuaLuu => "trungTen"),
  ]);

  await modal.getByRole("button", { name: /Xác nhận/ }).click();

  return dangDoi.catch(() => {
    throw new Error(
      `Lỗi: phiên "${account}" bấm Xác nhận nhưng không thấy toast "Thành công" ` +
        `lẫn thông báo "${THONG_BAO_TRUNG_TEN}"`,
    );
  });
};

/**
 * Mong muốn phía dữ liệu: trong BHS **chỉ có đúng 1 thư mục** mang tên `tenThuMuc`
 * (và đó cũng là item duy nhất của BHS — BHS dựng mới, loại `AUTO-TEST-TYPE` không có cấu trúc mẫu).
 */
const expectChiCo1ThuMuc = async (
  page: Page,
  recordUrl: string,
  tenThuMuc: string,
) => {
  await openBoHoSo(page, recordUrl);
  await openTabCauTrucHoSo(page);

  const row = getCauTrucRow(page, tenThuMuc);
  await expect(row, {
    message: `Lỗi: không thấy thư mục "${tenThuMuc}" trong bảng Cấu trúc hồ sơ — không phiên nào tạo được`,
  }).toBeVisible({ timeout: TIMEOUT.DATA_LOADING });

  await expect(
    getActiveTabPane(page).locator(".ant-table-tbody tr.ant-table-row"),
    {
      message:
        `Lỗi: bảng Cấu trúc hồ sơ không có đúng 1 thư mục — 2 phiên cùng lưu tên ` +
        `"${tenThuMuc}" nhưng app tạo nhiều hơn 1 item`,
    },
  ).toHaveCount(1, { timeout: TIMEOUT.DATA_LOADING });
};

/**
 * @param nguoiThamGia danh sách người thao tác đồng thời — phần tử **đầu tiên** là người tạo BHS,
 *   các phần tử sau được cấp **Quyền Owner ở BHS** ngay lúc tạo. Tất cả tạo **cùng 1 tên thư mục**.
 */
const runTest = async (maCase: string, nguoiThamGia: NguoiThamGia[]) => {
  const nguoiTaoBHS = nguoiThamGia[0];
  const goc = `AT-UC22-${maCase}-${Date.now()}`;
  const tenThuMuc = `${goc}-TM`; // 🚨 CÙNG TÊN cho mọi phiên — điểm mấu chốt của case 250

  let recordUrl = "";

  await test.step(
    `Tiền điều kiện: ${nguoiTaoBHS.account} tạo BHS "${goc}" (cấp Owner cho ` +
      `${nguoiThamGia
        .slice(1)
        .map((n) => n.account)
        .join(", ")}) → chuyển "Hoạt động"`,
    async () => {
      recordUrl = await createBoHoSo(
        nguoiTaoBHS.page,
        new PW(nguoiTaoBHS.page),
        goc,
        {
          peoplePickers: nguoiThamGia.slice(1).map((n) => ({
            testId: DOC_PERM.OWNER,
            account: n.account,
          })),
        },
      );

      // Vai không phải người tạo BHS chỉ mở được BHS ở trạng thái "Hoạt động"
      await chuyenHoatDong(nguoiTaoBHS.page, recordUrl);
    },
  );

  await test.step(
    `Thao tác 1: ${nguoiThamGia.length} trình duyệt cùng mở modal "Thêm thư mục" và chuẩn bị đầy đủ ` +
      `dữ liệu với CÙNG tên "${tenThuMuc}" — chưa lưu`,
    async () => {
      const modals = await Promise.all(
        nguoiThamGia.map((n) =>
          chuanBiDuLieuTaoThuMuc(n.page, recordUrl, tenThuMuc),
        ),
      );
      nguoiThamGia.forEach((n, i) => (n.modal = modals[i]));
    },
  );

  await test.step(
    `Thao tác 2: ${nguoiThamGia.length} trình duyệt bấm "Xác nhận" CÙNG LÚC (không biết trước phiên nào thắng)`,
    async () => {
      // Các callback chạy trong cùng 1 tick → 2 lệnh click phát đi đồng thời
      const ketQua = await Promise.all(
        nguoiThamGia.map((n) => bamLuuVaDoKetQua(n.page, n.modal!, n.account)),
      );
      nguoiThamGia.forEach((n, i) => (n.ketQua = ketQua[i]));

      // Chờ app ghi xong + refresh bảng trước khi điều hướng (cùng lý do với SAVE_SETTLE_WAIT
      // trong KHO-TAI-LIEU.functions.ts)
      await Promise.all(
        nguoiThamGia.map((n) => n.page.waitForTimeout(TIMEOUT.CONTROL_LOADING)),
      );
    },
  );

  await test.step(
    `Mong muốn 1: đúng 1 phiên lưu thành công, ${nguoiThamGia.length - 1} phiên còn lại báo ` +
      `"${THONG_BAO_TRUNG_TEN}"`,
    async () => {
      const moTa = nguoiThamGia
        .map((n) => `${n.account}=${n.ketQua}`)
        .join(", ");

      expect(
        nguoiThamGia.filter((n) => n.ketQua === "thanhCong").length,
        `Lỗi: phải có ĐÚNG 1 phiên tạo được thư mục "${tenThuMuc}". Kết quả thực tế: ${moTa}`,
      ).toBe(1);

      // Phiên thua phải hiện đúng nguyên văn thông báo QA yêu cầu
      for (const n of nguoiThamGia.filter((x) => x.ketQua === "trungTen")) {
        await expect(n.page.getByText(THONG_BAO_TRUNG_TEN).first(), {
          message:
            `Lỗi: phiên "${n.account}" bị chặn nhưng thông báo không đúng nguyên văn ` +
            `"${THONG_BAO_TRUNG_TEN}"`,
        }).toBeVisible({ timeout: TIMEOUT.CONTROL_LOADING });
      }
    },
  );

  await test.step(
    `Mong muốn 2: trong Bộ hồ sơ chỉ có ĐÚNG 1 thư mục "${tenThuMuc}"`,
    async () => {
      // Phiên thua vẫn đang mở modal "Thêm thư mục" → đóng lại trước khi điều hướng
      await Promise.all(
        nguoiThamGia.map((n) => dongModalThuMucNeuDangMo(n.page)),
      );

      await Promise.all(
        nguoiThamGia.map((n) =>
          expectChiCo1ThuMuc(n.page, recordUrl, tenThuMuc),
        ),
      );
    },
  );
};

test(
  "UC22 250 - 2 người cùng tạo mới thư mục tại cùng một thời điểm (cùng tên) - Thủ thư + Admin" +
    ACCOUNT.LIBRARIAN +
    ACCOUNT.ADMIN,
  async ({ librarian, admin }) => {
    await runTest("250", [
      { page: librarian, account: ACCOUNT.LIBRARIAN },
      { page: admin, account: ACCOUNT.ADMIN },
    ]);
  },
);
