import { expect, Page } from "@playwright/test";
import { TIMEOUT } from "../constant/timeout";
interface PWData {
  testId: string;
  value: string;
  extraLocator?: string;
}
export class PW {
  private page: Page;
  constructor(page: Page) {
    this.page = page;
  }
  isVisible(
    testId: string,
    timeout: number = TIMEOUT.ACTION_LOADING,
    message?: string,
  ) {
    return expect(this.getElement(testId), { message }).toBeVisible({
      timeout,
    });
  }

  async inputWithoutSelDuAn() {
    let count = 0;
    do {
      const visibleDuAn = await this.page.getByTestId("sel-duAn").isVisible();

      if (visibleDuAn) {
        await this.inputTreeDropDown("sel-loaiBoHoSo");
      } else {
        break;
      }
      count++;
      if (count > 10) {
        break;
      }
    } while (true);
  }
  async inputWithSelDuAn() {
    let count = 0;
    do {
      const visibleDuAn = await this.page.getByTestId("sel-duAn").isVisible();

      if (visibleDuAn) {
        await this.inputDropDownList("sel-duAn");
        break;
      } else {
        await this.inputTreeDropDown("sel-loaiBoHoSo");
      }
      count++;
      if (count > 10) {
        break;
      }
    } while (true);
  }

  async isEmpty(testId: string, timeout = TIMEOUT.VALIDATE_WAITING) {
    if (testId.startsWith("tree-sel-")) {
      await this.isTreeDropDownListEmpty(testId);
    } else if (testId.startsWith("sel-tags-")) {
      await expect(
        this.page.getByTestId(testId).locator(".ant-select-selection-item"),
      ).toHaveCount(0, { timeout });
    } else if (testId.startsWith("sel-")) {
      await this.isDropDownListEmpty(testId);
    } else if (testId.startsWith("txt-") || testId.startsWith("txa-")) {
      await this.isTextInputEmpty(testId);
    } else if (testId.startsWith("pp-multi-")) {
      await expect(
        this.page.getByTestId(testId).locator(".ant-tag"),
      ).toHaveCount(0, { timeout });
    } else {
      throw new Error(`Chưa có hàm kiểm tra isEmpty cho testId ${testId}`);
    }
  }
  async valueShouldBe(
    testId: string,
    expectedValue: string,
    timeout = TIMEOUT.VALIDATE_WAITING,
  ) {
    if (testId.startsWith("sel-") || testId.startsWith("tree-sel-")) {
      await expect(
        this.getElement(testId).locator(".ant-select-selection-item"),
      ).toHaveText(expectedValue, { timeout });
    }
    if (testId.startsWith("txt-") || testId.startsWith("txa-")) {
      await expect(this.getElement(testId)).toHaveValue(expectedValue, {
        timeout,
      });
    }
  }
  async valueShouldContain(
    testId: string,
    expectedValue: string,
    timeout = TIMEOUT.VALIDATE_WAITING,
  ) {
    if (testId.startsWith("sel-") || testId.startsWith("tree-sel-")) {
      await expect(
        this.getElement(testId).locator(".ant-select-selection-item"),
      ).toContainText(expectedValue, { timeout });
    }
    if (testId.startsWith("txt-") || testId.startsWith("txa-")) {
      await expect(this.getElement(testId)).toContainText(expectedValue, {
        timeout,
      });
    }
  }
  async isTextInputEmpty(testId: string) {
    await this.isVisible(testId);
    await expect(this.getElement(testId)).toHaveValue("");
  }
  async isTreeDropDownListEmpty(testId: string) {
    await this.isVisible(testId);
    await expect(
      this.getElement(testId).locator(".ant-select-selection-placeholder"),
    ).toBeVisible();
  }
  async isDropDownListEmpty(testId: string) {
    await this.isVisible(testId);
    await expect(
      this.getElement(testId).locator(".ant-select-selection-placeholder"),
    ).toBeVisible();
  }
  getElement(testId: string) {
    return this.page.getByTestId(testId);
  }
  async arrowDown() {
    this.page.keyboard.press("ArrowDown");
  }
  async enter() {
    return this.page.keyboard.press("Enter");
  }
  wait(timeout = 500) {
    return this.page.waitForTimeout(timeout);
  }
  async clickButton(testId: string) {
    return await this.getElement(testId).click();
  }
  async inputText(testId: string, text: string) {
    await this.getElement(testId).fill(text);
    // await expect(this.getElement(testId)).toContainText(text.trim());
  }
  async checkListValueDropdownListTags(
    testId: string,
    expectedValues: string[],
  ) {
    await this.getElement(testId).click();
    await expect(
      this.getElement(testId).locator(
        ".ant-select-selection-item-content:visible",
      ),
      {
        message: "Lỗi: Số lượng tags không giống số lượng mong muốn",
      },
    ).toHaveCount(expectedValues.length);
    for (const giaTri of expectedValues) {
      // Lọc ra cái option chứa giá trị đó và ép nó phải đang hiển thị trên màn hình
      await expect(
        this.getElement(testId)
          .locator(".ant-select-selection-item-content:visible")
          .filter({ hasText: giaTri.trim() })
          .first(),
        {
          message: "Lỗi: Giá trị mong đợi không đúng",
        },
      ).toBeVisible();
    }
  }
  async checkListValueDropdownList(testId: string, expectedValues: string[]) {
    await this.getElement(testId).click();
    await expect(
      this.page.locator(".ant-select-item-option-content:visible"),
    ).toHaveCount(expectedValues.length);
    for (const giaTri of expectedValues) {
      // Lọc ra cái option chứa giá trị đó và ép nó phải đang hiển thị trên màn hình
      await expect(
        this.page
          .locator(".ant-select-item-option-content")
          .filter({ hasText: giaTri })
          .first(),
      ).toBeVisible();
    }
  }

  async checkSearchDropdownHasOption(testId: string, keyword: string) {
    await this.isVisible(testId);

    await this.getElement(testId).getByRole("combobox").fill(keyword);
    await this.wait(2000); // Chờ dữ liệu load lại
    const danhSachOptions = this.getElement(testId).locator(
      ".ant-select-item-option-content",
    );

    // Tất cả options đều phải chứa keyword (kiểm tra cả bản gốc và bản không dấu của option)
    const total = await danhSachOptions.count();
    for (let i = 0; i < total; i++) {
      const optionText = await danhSachOptions.nth(i).innerText();
      const matched =
        optionText.includes(keyword) ||
        this.removeAccents(optionText).includes(keyword);
      expect(matched, {
        message: `Option "${optionText}" không chứa keyword "${keyword}"`,
      }).toBe(true);
    }
  }

  async checkSearchTreeDropdownHasOption(testId: string, keyword: string) {
    await this.isVisible(testId);

    await this.getElement(testId).getByRole("combobox").fill(keyword);

    const danhSachOptions = this.page.locator(".ant-select-tree-title");

    // Lọc ra các option chứa keyword, lấy cái đầu tiên tìm thấy và ép nó phải tồn tại
    await expect(
      danhSachOptions.filter({ hasText: keyword }).first(),
    ).toBeVisible();
  }

  async inputDropDownList(testId: string, optionText?: string) {
    if (!optionText || optionText.trim() === "") {
      await this.getElement(testId).click();
      await this.wait(TIMEOUT.CONTROL_LOADING);
      await this.enter();
      await expect(
        this.getElement(testId).locator(".ant-select-selection-item"),
        {
          message: `Dropdown [${testId}] vẫn rỗng sau khi chọn giá trị đầu tiên`,
        },
      ).toBeVisible({ timeout: TIMEOUT.VALIDATE_WAITING });
    } else {
      await this.getElement(testId).click();
      await this.page
        .locator(`.ant-select-item-option-content:text-is("${optionText}")`)
        .click();
      await expect(
        this.getElement(testId).locator(".ant-select-selection-item"),
        {
          message: `Dropdown [${testId}] không hiển thị giá trị "${optionText}" sau khi chọn`,
        },
      ).toHaveText(optionText, { timeout: TIMEOUT.VALIDATE_WAITING });
    }
  }
  async inputTreeDropDown(
    testId: string,
    optionText?: string,
    extraLocator?: string,
  ) {
    if (!optionText || optionText.trim() === "") {
      await this.getElement(testId).click();
      await this.wait(TIMEOUT.CONTROL_LOADING);
      await this.arrowDown();
      await this.enter();
      await expect(
        this.getElement(testId).locator(".ant-select-selection-item"),
        {
          message: `Tree dropdown [${testId}] vẫn rỗng sau khi chọn giá trị đầu tiên`,
        },
      ).toBeVisible({ timeout: TIMEOUT.VALIDATE_WAITING });
    } else {
      await this.getElement(testId).click();
      const locator = extraLocator
        ? this.page.locator(
            `.ant-select-tree-title ${extraLocator}:text-is("${optionText}")`,
          )
        : this.page.locator(`.ant-select-tree-title:text-is("${optionText}")`);
      await locator.click();
      await expect(
        this.getElement(testId).locator(".ant-select-selection-item"),
        {
          message: `Tree dropdown [${testId}] không hiển thị giá trị "${optionText}" sau khi chọn`,
        },
      ).toContainText(optionText, { timeout: TIMEOUT.VALIDATE_WAITING });
    }
  }
  async inputDropDownTagsList(testId: string, tags: string[]) {
    const element = this.getElement(testId);
    await element.click();
    // Nhập tag thứ 1
    for (const tag of tags) {
      await element.pressSequentially(tag);
      await this.wait(500); // Chờ dropdown load ra
      await this.enter();
    }
    this.checkListValueDropdownListTags(
      testId,
      tags.map((tag) => tag.trim()),
    );
  }
  async inputTextArea(testId: string, text: string) {
    await this.getElement(testId).fill(text);
    await expect(this.getElement(testId)).toHaveValue(text);
  }
  async inputDatetime(testId: string, datetime: string) {
    await this.clickButton(testId);
    await this.getElement(testId).fill(datetime);
    await this.enter();
    await expect(this.getElement(testId), {
      message: `Ô ngày tháng [${testId}] không hiển thị giá trị "${datetime}" sau khi nhập`,
    }).toHaveValue(datetime, { timeout: TIMEOUT.VALIDATE_WAITING });
  }

  /**
   *
   * @param order thứ tự của people picker trên form, tính từ 1 trở đi. Ví dụ: 1 = người tạo, 2 = người duyệt, v.v... (cái này để xác định được selector chính xác vì có thể có nhiều people picker trên form)
   * @param name text tìm kiếm user
   */
  async inputPeoplePicker(
    testId: string,
    value: string,
    additionalCount?: number,
  ) {
    const emails = value.split(",").map((email) => email.trim());
    const peoplePicker = this.getElement(testId);
    for (const email of emails) {
      await peoplePicker.locator(".ant-select-selection-search-input").click();
      await peoplePicker
        .locator(".ant-select-selection-search-input")
        .pressSequentially(email, { delay: 200 });
      await this.wait(5000); // Chờ kết quả load ra
      await this.enter();
    }
    // await expect(peoplePicker.locator(".ant-tag"), {
    //   message: `People picker [${testId}] không hiển thị đủ ${emails.length + (additionalCount || 0)} người sau khi chọn`,
    // }).toHaveCount(emails.length + (additionalCount || 0), {
    //   timeout: TIMEOUT.VALIDATE_WAITING,
    // });
  }

  async inputRelatedECM(testId: string, order?: number) {
    //Bấm nút thêm
    await this.clickButton(testId);
    //bấm vào ecm đầu tiên

    await this.page
      .locator(".ant-modal-root")
      .last()
      .locator(".ant-table-row")
      .first()
      .hover();
    await this.page
      .locator(".ant-modal-root")
      .last()
      .locator(".ant-table-row .ant-checkbox-wrapper")
      .first()
      .click();

    await this.page.getByTestId("btn-add-related").click();
    await this.isVisible("related-item-table");
  }

  async clearValue(testId: string) {
    if (testId.startsWith("sel-") || testId.startsWith("tree-sel-")) {
      await this.getElement(testId).locator(".ant-select-clear").click();
    }
    if (testId.startsWith("txt-") || testId.startsWith("txa-")) {
      await this.getElement(testId).fill("");
    }
  }

  async getValue(testId: string) {
    if (testId.startsWith("sel-") || testId.startsWith("tree-sel-")) {
      return await this.getElement(testId)
        .locator(".ant-select-selection-item")
        .innerText();
    }
  }

  private removeAccents(str: string): string {
    return str
      .normalize("NFD")
      .replace(/[\u0300-\u036f]/g, "")
      .toLowerCase();
  }

  async batchInput(data: PWData[], checkBranch = false) {
    for (const item of data) {
      // if (!item.value) continue;
      if (item.testId.startsWith("sel-tags")) {
        await this.inputDropDownTagsList(item.testId, item.value.split(","));
        continue;
      }
      if (item.testId.startsWith("txt-")) {
        await this.inputText(item.testId, item.value);
        continue;
      }
      if (item.testId.startsWith("txa-")) {
        await this.inputTextArea(item.testId, item.value);
        continue;
      }
      if (item.testId.startsWith("date-")) {
        await this.inputDatetime(item.testId, item.value);
        continue;
      }
      if (item.testId.startsWith("sel-")) {
        await this.inputDropDownList(item.testId, item.value);
        if (item.testId == "sel-loaiBoHoSo" && checkBranch) {
          //kiểm tra Loại hồ sơ có phải là loại dự án hay không
          const dropdownDuAn = this.getElement("sel-duAn");
          let dangHienThi = false;

          try {
            // Chờ tối đa 2 giây xem nó có ngoi lên không
            await dropdownDuAn.waitFor({ state: "visible", timeout: 2000 });
            dangHienThi = true;
          } catch (e) {
            dangHienThi = false; // Quá 2 giây không thấy -> Chắc chắn nó bị ẩn
          }

          if (dangHienThi) {
            await this.inputDropDownList("sel-duAn");
          }
        }
        if (item.testId == "sel-loaibohoso" && checkBranch) {
          await this.inputDropDownList("sel-duan");
        }
        continue;
      }
      if (item.testId.startsWith("tree-sel-")) {
        await this.inputTreeDropDown(
          item.testId,
          item.value,
          item.extraLocator,
        );
        continue;
      }
      if (item.testId.startsWith("pp-multi-")) {
        await this.inputPeoplePicker(item.testId, item.value);
        continue;
      }
      if (item.testId.startsWith("btn-add-related-ecm")) {
        await this.inputRelatedECM(item.testId);
        continue;
      }
    }
  }
}
