import { expect, Locator, Page } from "@playwright/test"
import { BasePage } from "./base.page";
import { getPropertyValues, modifierNullToDash } from "../utils/array";
import { LoginPage } from "./login.page";
import { globalSearchCaseUnarchive, globalSearchClientHadCaseUnarchive, globalSearchRequestUnarchive } from "./dashboard.snapshot";

export type TableData = {
    dataApi: any[];
    dataUI: string[];
}

export class DashboardPage extends BasePage {
    constructor(page: Page) {
        super(page)
    }

    pageFilter = [
        {
            name: "Case",
            link: "cases"
        },
        {
            name: "Request",
            link: "requests"
        },
        {
            name: "Client",
            link: "clients"
        },
        {
            name: "Business",
            link: "businesses"
        },
        {
            name: "Contact",
            link: "contacts"
        },
        {
            name: "Inbox",
            link: "requests/inbox"
        },
        {
            name: "Sent",
            link: "requests/sent"
        },
        {
            name: "Share with me Request",
            link: "shared/requests"
        }
    ]

    pages = [
        {
            name: "Cases",
            link: "cases"
        },
        {
            name: "Requests",
            link: "requests"
        },
        {
            name: "Clients",
            link: "clients"
        },
        {
            name: "Businesses",
            link: "businesses"
        },
        {
            name: "Contacts",
            link: "contacts"
        },
        {
            name: "Inbox",
            link: "requests/inbox"
        },
        {
            name: "Sent",
            link: "requests/sent"
        },
        {
            name: "My Company",
            link: "setting/my-company"
        },
        {
            name: "Members",
            link: "setting/members"
        },
        {
            name: "General",
            link: "general/hashtags"
        },
        {
            name: "Requests",
            link: "shared/requests"
        }
    ]

    devices = [
        { name: 'Tablet', viewport: { width: 768, height: 1024 } },
        { name: 'Phone', viewport: { width: 390, height: 844 } },
    ];

    filterDate = ['Year to Date'];

    navbarTabs = {
        tabs: [
            { name: "Home" },
            { name: "Case" },
            { name: "Request" },
            { name: "Client" },
            { name: "Business/Contact", subTabs: ["Business", "Contact"] },
            { name: "Inbox/Sent", subTabs: ["Inbox", "Sent"] },
            { name: "Settings", subTabs: ["My Company", "Member", "General"] },
            { name: "Shared with me", subTabs: ["Case", "Request"] }
        ]
    };

    get dashboardSnapshot() {
        return {
            globalSearchCaseUnarchive: globalSearchCaseUnarchive,
            globalSearchRequestUnarchive: globalSearchRequestUnarchive,
            globalSearchClientHadCaseUnarchive: globalSearchClientHadCaseUnarchive
        }
    }

    get dashboardLoc() {
        return {
            chart: {
                boxDateFilter: (date: string) => this.genLoc(`//div[contains(@class,'date-option') and text()='${date}']`),

                // Chart Status Detail
                chartStatusDetailColumnAllRequestFirst: this.genLoc(`g:nth-child(12) > g > path`).first(),
                chartStatusDetailColumnAllRequest: (index: number) => this.genLoc(`g:nth-child(12) > g > path:nth-child(${index})`),
                chartStatusDetailColumnFist: this.genLoc(`g:nth-child(12) > g:nth-child(2) > path`),
                chartStatusDetailColumn: (index: number) => this.genLoc(`g:nth-child(12) > g > path:nth-child(${index})`),

                // Chart Status
                chartStatusColumn: (index: number) => this.genLoc(`g:nth-child(2) > path:nth-child(${index})`),
                chartStatusColumnAllRequestFist: this.genLoc(`.apexcharts-bar-area`),
                chartStatusColumnAllRequest: (index: number) => this.genLoc(`.apexcharts-series > path:nth-child(${index})`),
            },
            thumbImagePosted: this.genLoc("//div[@class='letter-thumb-image']"),

            //Feedback
            inputUploadFileFeedback: this.genLoc("//input[@id='attachmentsInput']"),
            btnFeedBack: this.genLoc("//div[@id='button-feedback']"),
            btnViewListFeedback: this.genLoc("//a[@class='inform-new-message']"),
            textAreaMessage: this.genLoc("//textarea[@id='text_area_message']"),
            textAreaFeedback: this.genLoc("//textarea[@id='text_area_message-chat']"),
            btnSendFeedback: this.genLoc("//a[@data-test-id='btn-feedback-send-message-icon']"),
            btnAddNewFeedBack: this.genLoc("//button[@data-test-id='btn-modal-add-new-feedback-submit']"),
            btnSoundGood: this.genLoc("//button[@data-test-id='btn-modal-thank-you-feedback-sound-good']"),
            messageFeedback: this.genLoc("//div[contains(@class,'message sent')]"),

            tabMyRequest: this.genLoc("//p[@class='title-shared tab-guide-me']"),
            tabMyRequestActive: this.genLoc("//div[@class='my-report cursor-pointer tab-shared tab-active']"),
            tabAllRequest: this.genLoc("//p[@class='title-shared tab-guide-my-business']"),
            tabAllRequestActive: this.genLoc("//div[@class='my-report cursor-pointer tab-shared tab-active']"),
            iconSearchGlobal: this.genLoc("(//div[@class='input-group input-group-flat input-group-search input-group-search-global'])[2]"),
            listBusinesses: this.genLoc("//a[contains(@class,'logo-contact')]"),
            boxNoDataGlobalSearch: this.genLoc("//div[@class='box-no-search-results']"),
            inputSearchGlobal: this.genLoc("(//input[@class='form-control input-search-global'])[2]"),
            msgAddBusinessInDetail: this.genLoc("//p[text()='Participants Added Successfully!']"),
            msgCreateSuccess: this.genLoc("//p[text()='Added New Successfully!']"),
            msgSuccess: this.genLoc("//p[text()='Updated Successfully!']"),
            msgMaximumUpload: this.genLoc("//p[text()='Please upload a maximum of 6 files']"),
            msgMaximum5MB: this.genLoc("//p[text()='Maximum file size is 5MB. If uploading a photo, try reducing its resolution.']"),
            msgInvalidFileType: this.genLoc("//p[text()='Invalid file type. Only images, PDF, Excel and Word files are allowed.']"),
            msgRemoveBussinessSuccess: this.genLoc("//p[text()='Business Removed Successfully!']"),

            notification: {
                btnCloseNotification: this.genLoc("//div[@class='notification-popup-latest-wrapper displayed']/descendant::div[@class='close-btn']"),
                backButton: this.genLoc("//div[contains(@class,'back-btn')]"),
                noNotificationsYet: this.genLoc(`//p[text()="When you get notifications, they'll show up here"]`),
                tabNotifications: (tabName: string) => this.genLoc(`//span[@data-bs-original-title='${tabName}']`),
                listNotifications: this.genLoc("//div[@class='notication-content latest-notification']"),
                timeCount: this.genLoc("//div[@class='box-content']/span[@class='date']"),
                numberResultNotifications: this.genLoc("//span[@class='search-notfication-result']"),
                numberResultNotificationsWhenGrooup: this.genLoc("//div[@class='search-notfication-wrap group']/descendant::span[@class='search-notfication-result']"),
                inputSearchNotifications: this.genLoc("//input[contains(@class,'search-notfication-input')]"),
                btnNotification: this.genLoc("//a[@class='notification-bell icon-navbar-menu-mobile']"),
                signalNoti: this.genLoc("//div[@class='notify-round notify-round-red has-notify-icon']"),
                desNotifications: this.genLoc("//div[@class='box-content']/p"),
                notiSideBar: this.genLoc("//div[@class='box-notification-content']"),
                requestID: (id: string) => this.genLoc(`//div[@class='box-notification-content-item box-notification-by-entity-type active']/descendant::span[contains(.,'${id}')]`),
                notificationType: (type: string) => this.genLoc(`//span[@data-bs-original-title='${type}']`),
                listNotiReminder: this.genLoc("//div[@class='notification-url cursor-pointer']")
            },
            buttonLinkByText: (text: string) => this.genLoc(`//a[text()='${text}']`),
            buttonByText: (text: string) => this.genLoc(`//button[text()='${text}']`),
            notiCard: this.genLoc("//div[@class='notification-latest-item cursor-pointer']"),
            root: this.genLoc("//div[@id='root']"),
            listing: {
                heading: {
                    headingColumn: (name: string) => this.genLoc(`//th[contains(@class, 'title-table') and contains(normalize-space(), '${name}')]`).first(),
                },
                tableHeading: {
                    btnThreedot: this.genLoc(`//div[@class='dropdown d-flex align-items-center']`),
                    dropdownMenu: (name: string) => this.genLoc(`//span[contains(@class, 'dropdown-item') and normalize-space()='${name}']`),
                    dropdownMenuWithATag: (name: string) => this.genLoc(`//a[contains(@class, 'dropdown-item') and normalize-space()='${name}']`)
                },
                tableFiltering: {},
                table: {
                    tableRowTd: (data: string) => this.genLoc(`//td[text()='${data}']`),
                },
                popup: {
                    customizedColumn: {
                        columnCheckboxRequestCase: (name: string) => this.genLoc(`//label[normalize-space()='${name}']/preceding-sibling::input`),
                        columnCheckbox: (name: string) => this.genLoc(`//label[normalize-space()='${name}']/preceding-sibling::input`),
                        btnApply: this.genLoc(`//div[contains(@class, 'modal-content') and .//h3[contains(normalize-space(),'Customized Columns')]]//div[contains(@class, 'modal-footer')]//button[text()='Apply']`),
                    }
                }
            },
            creating: {
                errorInput: (errText: string) => this.genLoc(`//span[contains(text(), '${errText}')]`),
            },
            updating: {},
            header: {
                profile: {
                    dropdown: this.genLoc(`//header//div[contains(@class, 'nav-item dropdown') and ./a[@aria-label="Open user menu"]]`),
                    menuItemProfile: this.genLoc(`//div[contains(@class, 'dropdown-menu') and contains(@class, 'show')]//a[contains(text(), 'Profile')]`),
                    menuItemChangePassword: this.genLoc(`//div[contains(@class, 'dropdown-menu') and contains(@class, 'show')]//a[contains(text(), 'Change password')]`),
                    menuItemLogout: this.genLoc(`//div[contains(@class, 'dropdown-menu') and contains(@class, 'show')]//a[contains(text(), 'Logout')]`),
                }
            },

            cart: {
                buttonCart: this.genLoc("//div[@class='cart-header-wrapper']"),
                requestButtonCart: this.genLoc("//a[contains(@href,'is_submission_cart')]"),
                requestButtonEditCart: this.genLoc("//a[contains(@href,'is_bulk_edit_cart')]"),
                btnDeleteAllRequestCart: (type: string) => this.genLoc(`//a[contains(@href, '${type}')]/*[local-name()='svg']`),
                totalRequestsInCart: (total: string) => this.genLoc(`//a[contains(@href,'is_submission_cart') and contains(.,'${total}')]`)
            },

            // Card header 
            contentHeader: {
                container: this.genLoc(`//div[contains(@class, 'card-header')]`),
            },

            // Personal search
            search: {
                container: this.genLoc(`//div[contains(@class, 'personal-search')]`),
                perPage: {
                    container: this.genLoc(`//div[contains(@class, 'box-count-show per-page-input')]`),
                    input: this.genLoc(`//div[contains(@class, 'box-count-show per-page-input')]//input`),
                },
                filter: {
                    btn: this.genLoc(`//div[contains(@class, 'personal-search')]//button[contains(@class, 'btn-filter') and normalize-space()='Filter']`).first(),
                    popup: {
                        container: this.genLoc(`//div[contains(@class, 'modal-content') and .//h3[contains(normalize-space(),'Filter')]]`),

                        iconClose: this.genLoc(`//div[contains(@class, 'modal-content') and .//h3[contains(normalize-space(),'Filter')]]//button[contains(@class, 'btn-close')]`),
                        btnClose: this.genLoc(`//div[contains(@class, 'modal-content') and .//h3[contains(normalize-space(),'Filter')]]//div[contains(@class, 'modal-footer')]//button[text()='Cancel']`),
                        btnReset: this.genLoc(`//div[contains(@class, 'modal-content') and .//h3[contains(normalize-space(),'Filter')]]//div[contains(@class, 'modal-footer')]//button[text()='Reset']`),
                        btnApply: this.genLoc(`//div[contains(@class, 'modal-content') and .//h3[contains(normalize-space(),'Filter')]]//div[contains(@class, 'modal-footer')]//button[text()='Apply']`),

                        inputField: (name: string) => this.genLoc(`//div[contains(@class, 'modal-content') and .//h3[contains(normalize-space(),'Filter')]]//div[contains(@class, 'modal-body')]//input[@name='${name}']`),
                        optionInput: (option: string) => this.genLoc(`//div[@class='title-option' and text()='${option}']`)
                    }
                },
                inputSearch: this.genLoc(`//div[contains(@class, 'personal-search')]//input[@placeholder='Search...']`),
            },

            table: {
                boxSelectedFilter: (dataFilter: string) => this.genLoc(`//div[contains(@class,'box-selected')]/div[text()='${dataFilter}']`),
                rowsInTable: this.genLoc("//table[contains(@class, 'table-business-client')]//tbody/tr"),
                dataRowsInTable: this.genLoc("//tbody/tr"),
                container: this.genLoc(`//table[contains(@class, 'table-business-client')]`),
                headingColumn: (name: string) => this.genLoc(`//th[contains(@class, 'title-table') and contains(normalize-space(), '${name}')]`).first(),
                loadingSkeleton: this.genLoc(`//table[contains(@class, 'table-business-client') and contains(@class, 'loading')]`),
                pinItem: (rowIndex: number) => this.genLoc(`//table[contains(@class, 'table-business-client')]//tr[${rowIndex}]//a[contains(@class, 'cursor-pointer')]`),

                itemInRowShareRequest: (name: string) => this.genLoc(`//a/span[text()='${name}']`),
                itemInRow: (name: string) => this.genLoc(`(//a[text()='${name}'])[1] | (//span[text()='${name}'])[1][not(//a[text()='${name}'])]`),
                btnThreeDot: this.genLoc("//div[@class='dropdown d-flex align-items-center']"),
                entriesTotal: this.genLoc(`//div[contains(@class, 'pagination-in-business')]`),
                noResult: this.genLoc(`//div[contains(text(), 'No matching results found. Try again.')]`),
                pagingSummary: this.genLoc("//p[@class='m-0 text-muted f-14 cl-gray info']")
            },
            dropdownItem: (name: string) => this.genLoc(`//li[@class='dropdown-item' and ./span[normalize-space()='${name}']]`),
            dataExistInTableRow: (name: string) => this.genLoc(`//a[text()='${name}']`),
            buttonUpdate: this.genLoc("//button[@class='btn d-flex align-items-center ga-8 btn-primary']"),
            buttonUpdateInForm: this.genLoc("//div[@class='box-btn-reset-apply-filter']/button[text()='Update']"),
            btnClosePopupNotification: this.genLoc(`//button[@class='my-snackbar__close']`),

            modal: {
                tag: (tagName: string) => this.genLoc(`//div[@class='box-selected']/descendant::span[text()='${tagName}']`),
                inputContainByID: (id: string) => this.genLoc(`//input[contains(@id,'${id}')]`),
                inputByID: (id: string) => this.genLoc(`//input[@id='${id}']`),
                inputByClass: (className: string) => this.genLoc(`//input[(@class='${className}')]`),
                duplicateErrMsg: this.genLoc("//p[contains(text(),'Duplicate entry matching your input.')]"),
                errorMessage: (message: string) => this.genLoc(`//div[@class='error-message' and text()='${message}']`),
                errorMessageSpan: (message: string) => this.genLoc(`//span[@class='error d-block error-message' and text()='${message}']`),
                inputByPlaceHolder: (placeHolder: string) => this.genLoc(`//input[@placeholder='${placeHolder}']`),
                headerModalh2: (title: string) => this.genLoc(`//div/h2[text()='${title}']`),
                headerModal: (title: string) => this.genLoc(`//div/h3[text()='${title}']`),
                headerModalContainText: (title: string) => this.genLoc(`//div/h3[contains(text(),'${title}')]`),
                button: {
                    unArchive: this.genLoc("//button[text()='Unarchive']"),
                    archive: this.genLoc("//button[text()='Archive']"),
                    acceptArchive: this.genLoc("//button[text()='Yes']"),
                }
            },

            detail: {
                statusDetailSpan: this.genLoc("//li[@class='dropdown-select__filter-selected cursor-pointer']/span"),
                statusDetailsInput: this.genLoc("//li[@class='dropdown-select__filter-selected cursor-pointer']"),
                tabName: (tabName: string) => this.genLoc(`//p[@class='tab-name' and text()='${tabName}']`),
                button: {
                    back: this.genLoc("//button[text()='Back']")
                }
            },

            notificationUnfollow: this.genLoc(`//p[contains(text(),'Unfollow Successfully!')]`),
            noOptionFound: this.genLoc("//div[text()='No options found.']"),
            notificationUpdateSuccess: this.genLoc(`//p[contains(text(),'Updated Successfully!')]`),
            namePage: this.genLoc("//span[@class='line-height-normal']"),
            monitorTab: (tab: string) => this.genLoc(`//p[@data-user-tracking="${tab}"]`),
            navLink: (name: string) => this.genLoc(`(//span[@class='nav-link-title' and contains(text(),'${name}')])[2]`),
            subNavLink: (name: string) => this.genLoc(`(//div[@class='dropdown-menu-column']//a[normalize-space()='${name}'])[2]`),
            showNavBarIcon: this.genLoc("//span[contains(@class,'navbar-show-menu-icon')]"),

            filterMonitor: {
                arrowDropdown: (index: number) => this.genLoc(`(//span[@class='arrow-icon'])[${index}]`),
                dropdownFilterOption: (option: string) => this.genLoc(`//span[text()='${option}']/parent::li`),
                itemChartTeamRequest: this.genLoc("(//*[@seriesName='TeamxRequest'])[3]"),
                itemChartShareWithTeam: this.genLoc("((//*[@seriesName='SharedxWithxTeam'])[3]//*[name()='path'])[2]"),
                tabRequest: (tab: string) => this.genLoc(`//div[text()="${tab}"]`),
                requestTypeNumber: this.genLoc("//div[contains(@class,'box-desc')]/h2")
            },

            // OTP
            inputCode: this.genLoc("//input[@name='token']"),

            tag: {
                divTag: this.genLoc("//div[@class='box-contact-tag-item box-hashtag-item']"),
                boxTag: this.genLoc("//div[@class='box-contact-tag-item box-hashtag-item']"),
                inputTag: this.genLoc("//input[@id='multi_select_tags']"),
                errorMaxTag: this.genLoc("//span[contains(@class,'error d-block error-message')]"),
                btnSaveTag: this.genLoc("//button[@class='btn btn-primary btn-contact-tag']"),
                listRemoveTag: this.genLoc("//div[@class='box-selected']/span[contains(@class,'cursor-pointer')]"),
                listTagDetail: this.genLoc("//div[@class='list-request-tag']/p"),
                btnClearAllTag: this.genLoc("//div[@class='multi-select']/descendant::button[@class='clear-btn']"),
            },

            // Common xpath
            common: {
                divText: (text: string) => this.genLoc(`//div[text()='${text}']`),
                aText: (text: string) => this.genLoc(`//a[text()='${text}']`),
                pTextContains: (text: string) => this.genLoc(`//p[contains(text(),"${text}")]`),
                popUpConfirm: this.genLoc("//button[@class='swal2-confirm swal2-styled']"),
                pText: (text: string) => this.genLoc(`//p[text()='${text}']`),
                spanText: (text: string) => this.genLoc(`//span[text()='${text}']`)
            },

            comment: {
                btnSubmitComment: this.genLoc("//button[@class='submit-send-comment']"),
                inputComment: this.genLoc("//textarea[@id='text_area_content']"),
                msgCreateCmtSuccess: this.genLoc("//p[text()='Added New Successfully!']"),
                cmtInPost: this.genLoc("(//div[contains(@class,'shadow-body box-attachment-tour list-letters')])[1]/descendant::span[contains(@class,'text-content-comment')]")
            },

            discussion: {
                btnCreateDiscussion: this.genLoc("(//span[@data-user-tracking='BTN_SHOW_CREATE_REQUEST_DISCUSSION_MODAL'])[1]"),
                inputTitle: this.genLoc("//input[@name='title']"),
                msgFirstDis: this.genLoc("#modal-start-discussion-request #text_area_content"),
                dropdownMemberShare: (member: string) => this.genLoc(`//div[text()='${member}']/parent::div[contains(@class,'box-text box-text-business')]`),
                msgCreateSucess: this.genLoc("//p[text()='Created successfully!']"),
                titleDiscussion: (title: string) => this.genLoc(`//span[text()='${title}']/ancestor::div[@class='request-discussion-wrapper']`),
                titleInDiscussion: (title: string) => this.genLoc(`//span[text()='${title}']/ancestor::div[@class='request-discussion-wrapper']`),
                titleDiscussionToClick: (title: string) => this.genLoc(`//span[text()='${title}']/ancestor::div[@class='request-discussion-wrapper']/descendant::span[text()='${title}']`),
                messageInDiscussion: (message: string) => this.genLoc(`//div[text()='${message}']`),
                chatOnlyYou: (accountName: string) => this.genLoc(`//span[@class='title-conversation' and text()='${accountName} (You)']`),
                inputMessage: this.genLoc("//input[@placeholder='Message']"),
                btnSendMsg: this.genLoc("//button[@class='btn-send-message btn-message']"),
                btnThreeDotsActionDiscussion: this.genLoc("//div[@class='dropdown ms-auto']"),
                btnThreeDotsReplyMessage: this.genLoc("//div[contains(@class,'icon-more-message')]"),
                btnDeleteMessage: this.genLoc("//span[@class='dropdown-item btn-message-discussion btn-remove-message-discussion cursor-pointer']"),
                btnReplyMessage: this.genLoc("//div[@class='dropdown-menu dropdown-menu-end box-dropdown-btn show']"),
                contentReply: this.genLoc("//p[@class='content-reply']"),
                labelWasReply: (message: string) => this.genLoc(`//div[text()='${message}']/ancestor::div[@class='item-message']/descendant::span[@class='reply-owner']`),
                newestDiscussion: this.genLoc("(//div[contains(@class,'item-content')])[2]"),
                inputMsgDis: this.genLoc("//input[@placeholder='Message']"),

            },

            status: {
                btnOpenStatusDetailDropdown: this.genLoc("//div[contains(@class,'box-resolution-request')]/descendant::li[contains(@class,'dropdown-select__filter-selected')]"),
                statusDetailOption: (statusDetail: string) => this.genLoc(`//li[contains(@class,'dropdown-select__select-option')]/descendant::span[text()='${statusDetail}']`),
                statusText: this.genLoc("//div[contains(@class,'request-status')]/descendant::span[contains(@class,'status-rq')]/descendant::span[contains(@class,'text-ellipsis')]")
            },

            modalActivity: {
                listActivity: this.genLoc("//div[@class='box-modal-activity-history activity-history']"),
                btnClose: this.genLoc("//button[@data-user-tracking='BTN_SHOW_CLONE_REQUEST_MODAL']"),
            },
        }
    }

    async navigateToMenu(menuName: "Profile" | "ChangePassword" | "Logout") {
        await this.dashboardLoc.header.profile.dropdown.waitFor({ state: 'visible' });
        await this.dashboardLoc.header.profile.dropdown.click({ force: true });
        await this.dashboardLoc.header.profile[`menuItem${menuName}`].click();
    }

    async logout() {
        await this.go('logout');
    }

    async openThreedotMenu(menuName: string, threeDotLocator: Locator | null = null, buttonHref?: boolean) {
        const tableHeadingLoc = this.dashboardLoc.listing.tableHeading;
        if (threeDotLocator) {
            await threeDotLocator.click();
        } else {
            await tableHeadingLoc.btnThreedot.click();
        }
        await this.waitForSecond(1.5);
        buttonHref ? await tableHeadingLoc.dropdownMenuWithATag(menuName).click() : await tableHeadingLoc.dropdownMenu(menuName).first().click();
    }

    async pinItem(rowIndex: number) {
        await this.dashboardLoc.table.pinItem(rowIndex).click();
    }
    async getTableHeadingOrder(name: string): Promise<number> {
        const headings = await this.dashboardLoc.table.container.locator('thead').locator('th').all();
        for (let i = 0; i < headings.length; i++) {
            const text = await headings[i].textContent();
            if (text?.trim() === name) {
                return i;
            }
        }
        return -1;
    }

    async getTableData(columnIndex: number, haveVisibleRow?: boolean, isTableBulkEdit?: boolean): Promise<string[]> {
        const rows = isTableBulkEdit ? await this.page.locator("//table[contains(@class, 'card-table-bulk')]").locator('tbody').locator('tr').all() : await this.dashboardLoc.table.container.locator('tbody').locator('tr').all()
        // const rows = await this.dashboardLoc.table.container.locator('tbody').locator('tr').all();
        const rowsLength = !haveVisibleRow ? rows.length : 10;
        const data: string[] = [];
        for (let i = 0; i < rowsLength; i++) {
            const cells = await rows[i].locator('td').all();
            const cellText = await cells[columnIndex].textContent();
            const trimmedText = cellText?.trim() || '';
            if (trimmedText === 'Missing') {
                data.push('--');
            } else {
                data.push(trimmedText);
            }
        }
        return data;
    }

    async getDataInTable(displayName: string, propertyName: string, tableApiUrl: string, haveVisibleRow?: boolean): Promise<TableData> {
        // Get data from table
        const order = await this.getTableHeadingOrder(displayName);
        expect(order, `Column ${displayName} should be visible`).toBeGreaterThan(-1);
        await this.waitForSecond(3); // TODO: refactor to wait api completed or table rendered
        const data = await this.getTableData(order, haveVisibleRow);

        // Compare with data from stat
        const rawData = await this.page.request.get(tableApiUrl);
        const apiData = await rawData.json();
        const apiDataValues = getPropertyValues(apiData.data, propertyName, modifierNullToDash);
        console.log(apiDataValues);
        console.log(data);
        return { dataApi: apiDataValues, dataUI: data };
    }

    async getTableRowIndexByColumnValue(columnIndex: number, value: string): Promise<number> {
        const rows = await this.dashboardLoc.table.container.locator('tbody').locator('tr').all();
        for (let i = 0; i < rows.length; i++) {
            const cells = await rows[i].locator('td').all();
            const cellText = await cells[columnIndex].textContent();
            if (cellText?.trim() === value) {
                return i;
            }
        }
        return -1;
    }

    async search(keyword: string) {
        await this.dashboardLoc.search.inputSearch.fill(keyword);
        await this.dashboardLoc.search.inputSearch.press('Enter');
    }

    async getTableRowCount(): Promise<number> {
        return await this.dashboardLoc.table.container.locator('tbody').locator('tr').count();
    }

    async resetToDefaultColumn(cusomizeColumn: any[], index: number = 2): Promise<void> {
        const customizedColumn = 'Customized Columns';
        const customThreeDotLocator = this.genLoc(`(//div[contains(@class, 'card-header')]//span[contains(@class, 'cursor-pointer')])[${index}]`);
        const customizedColumns = cusomizeColumn.filter((column: { default: any; }) => !column.default);
        await this.openThreedotMenu(customizedColumn, customThreeDotLocator);
        for (let i = 0; i < customizedColumns.length; i++) {
            await this.dashboardLoc.listing.popup.customizedColumn.columnCheckbox(customizedColumns[i].name).setChecked(false);
        }
        await this.dashboardLoc.listing.popup.customizedColumn.btnApply.click();
    }

    async getDetailCase(caseID: string, archiveBy?: string): Promise<void> {
        if (archiveBy) {
            await this.dashboardLoc.search.filter.btn.click();
            await this.dashboardLoc.search.filter.popup.inputField("is_archived").click();
            await this.dashboardLoc.search.filter.popup.optionInput(archiveBy).click();
            await this.dashboardLoc.search.filter.popup.btnApply.click();
        }
        await this.dashboardLoc.search.inputSearch.fill(caseID);
        await this.waitForSecond(2);
        await this.dashboardLoc.table.itemInRow(caseID).first().waitFor({ state: 'visible' });
        await this.dashboardLoc.table.itemInRow(caseID).first().click({ force: true });
    }

    async changeUser(username: string, password: string, page: Page): Promise<void> {
        let loginPage = new LoginPage(page);
        await this.logout();
        await page.context().clearCookies();
        await page.evaluate(() => {
            localStorage.clear();
            sessionStorage.clear();
        });
        await page.reload();
        await loginPage.open();
        await loginPage.login(username, password);
    }

    async checkNotification(type: string, gotoFirstNoti?: boolean): Promise<void> {
        try {
            const closeButton = await this.page.waitForSelector(
                `//div[@class='notification-popup-latest-wrapper displayed']/descendant::div[@class='close-btn']`,
                { state: 'visible', timeout: 2000 }
            );
            await closeButton.click();
        } catch (error) {
            console.log('No notification popup appeared');
        }

        await this.dashboardLoc.notification.btnNotification.waitFor({ state: 'visible' });
        await this.dashboardLoc.notification.btnNotification.click({ force: true });
        await this.dashboardLoc.notification.notificationType(type).click();
        if (gotoFirstNoti) {
            await this.dashboardLoc.notification.listNotiReminder.first().click();
        }
    }

    async addMultipleTags(tagCount: number, tagPrefix: string = "test"): Promise<void> {
        await this.dashboardLoc.tag.divTag.click();
        await this.dashboardLoc.tag.inputTag.first().waitFor({ state: 'visible' });
        for (let i = 0; i < tagCount; i++) {
            await this.dashboardLoc.tag.inputTag.first().fill(`${tagPrefix}${i}`);
            await this.dashboardLoc.tag.inputTag.first().press("Enter");
        }
    }

    async saveTags(): Promise<void> {
        await this.dashboardLoc.tag.btnSaveTag.click();
        await this.waitForSecond(1);
    }

    async getTagCount(): Promise<number> {
        return (await this.dashboardLoc.tag.listTagDetail.all()).length;
    }

    async removeOneTag(): Promise<void> {
        await this.dashboardLoc.tag.listRemoveTag.first().click();
    }

    async clearAllTags(): Promise<void> {
        await this.dashboardLoc.tag.listTagDetail.first().click();
        await this.dashboardLoc.tag.btnClearAllTag.waitFor({ state: 'visible' });
        await this.waitForSecond(1);
        await this.dashboardLoc.tag.btnClearAllTag.click({ force: true });
    }

    async showEntries(entries: Number) {
        const inputShowSelector = `//input[@id='input_per_page']`;
        await this.page.locator(inputShowSelector).click();
        await this.page.locator(inputShowSelector).fill(entries.toString());
        await this.page.waitForLoadState("domcontentloaded");
    }

    /**
     * Universal bulk edit verification function with dynamic parameters
     * Optimized for reuse across multiple test cases and page types
     * @param tabName - The tab name to click and verify ("Contacts", "Cases", "Request", etc.)
     * @param entryCount - Number of entries to show in the table
     * @param detailPageName - Name of the detail page for back navigation verification
     * @param shouldNavigateBack - Whether to navigate back after verification (default: true)
     */
    async verifyBulkEditForTab(
        tabName: string,
        entryCount: number,
        detailPageName: string,
        shouldNavigateBack: boolean = true
    ): Promise<void> {
        // Navigate to tab and show entries
        await this.dashboardLoc.detail.tabName(tabName).waitFor({ state: 'visible' });
        await this.waitForSecond(1.5);
        await this.dashboardLoc.detail.tabName(tabName).click({ force: true });
        await this.showEntries(entryCount);
        await this.waitForSecond(2);

        // Get original table data
        const originalData = await this.getTableData(0);

        // Click Bulk Edit and verify
        await this.dashboardLoc.buttonByText("Bulk Edit").click();
        await this.dashboardLoc.common.spanText("Bulk Edit").first().waitFor({ state: 'visible' });
        await this.waitForSecond(2);

        // Get bulk edit table data and compare
        const bulkEditData = await this.getTableData(1, false, true);

        // Data validation (without expect as requested)
        if (JSON.stringify(originalData) !== JSON.stringify(bulkEditData)) {
            throw new Error(`Bulk edit data mismatch in ${tabName} tab: Original ${JSON.stringify(originalData)} vs Bulk Edit ${JSON.stringify(bulkEditData)}`);
        }

        // Navigate back if required
        if (shouldNavigateBack) {
            await this.dashboardLoc.buttonByText("Back").first().click();
            await this.dashboardLoc.common.spanText(detailPageName).waitFor({ state: 'visible' });
        }
    }

    // Add a comment to the newest post
    async addCommentToNewestPost(content: string): Promise<void> {
        await this.dashboardLoc.comment.cmtInPost.first().waitFor({ state: 'visible' });
        await this.dashboardLoc.comment.cmtInPost.first().click();
        await this.dashboardLoc.comment.inputComment.first().fill(content);
        await this.waitForSecond(1);
        await this.dashboardLoc.comment.btnSubmitComment.first().click({ force: true });
        await this.dashboardLoc.comment.msgCreateCmtSuccess.waitFor({ state: 'visible' })
    }

    async createDiscussion(title: string, memberName: string, message: string): Promise<void> {
        await this.dashboardLoc.discussion.btnCreateDiscussion.click();
        await this.dashboardLoc.modal.headerModal("Start a discussion").waitFor({ state: 'visible' });
        await this.dashboardLoc.discussion.inputTitle.fill(title);
        await this.dashboardLoc.modal.inputByPlaceHolder("Type or select an email address").nth(1).fill(memberName);
        await this.dashboardLoc.discussion.dropdownMemberShare(memberName).waitFor({ state: "visible", timeout: 15_000 });
        await this.waitForSecond(2);
        await this.dashboardLoc.discussion.dropdownMemberShare(memberName).click();
        await this.dashboardLoc.modal.headerModal("Start a discussion").click();
        await this.dashboardLoc.discussion.msgFirstDis.fill(message);
        await this.dashboardLoc.modal.headerModal("Start a discussion").click();
        await this.dashboardLoc.buttonByText("Start").click();
        await this.dashboardLoc.modal.headerModal("Start a discussion").waitFor({ state: 'hidden' });
        await this.dashboardLoc.msgCreateSuccess.waitFor({ state: 'visible' });
    }

    async addMessageToDiscussion(message: string): Promise<void> {
        await this.dashboardLoc.discussion.inputMessage.fill(message);
        await this.waitForSecond(1);
        await this.dashboardLoc.discussion.btnSendMsg.click();
        await this.waitForSecond(2);
    }

    async getNewestRequestHistory(): Promise<string> {
        await this.dashboardLoc.buttonByText("Activity").click();
        const textActivity = await this.dashboardLoc.modalActivity.listActivity.first().innerText();
        await this.dashboardLoc.modalActivity.btnClose.click();
        return textActivity.replace(/\s+/g, ' ').trim();
    }

    async getRowCount(): Promise<number> {
        return this.dashboardLoc.table.rowsInTable.count();
    }

    async openGlobalSearch() {
        await expect(this.dashboardLoc.iconSearchGlobal).toBeVisible();
        await this.dashboardLoc.iconSearchGlobal.click();
    }

    async switchTab(targetTab: 'My' | 'All'): Promise<void> {
        if (targetTab === 'My') {
            await this.dashboardLoc.tabMyRequest.click();
        } else {
            await this.dashboardLoc.tabAllRequest.click();
        }
    }

    async getExpectedResultText(count: number): Promise<string> {
        return count > 1
            ? `There are ${count} results found.`
            : `There are ${count} result found.`;
    }
}
