import { expect, type Locator, type Page } from '@playwright/test';
import { BasePage } from './BasePage';
import { AppointmentTimeslot, ServicePackage, Soft, VehicleDamage } from 'safelite-playwright-core';
import { ProgressBarPercentages } from 'framework/localTypes/Enums';
import { PaymentMethod } from "framework/localTypes/Enums";
import { step } from 'framework/localTypes/Step';
import { ITestData } from 'framework/TestData';

export class ServicePackagesPage extends BasePage {
    readonly page: Page;
    readonly standardPackageButton: Locator;
    readonly premiumPackageButton: Locator;
    readonly glassOnlyButton: Locator;
    readonly payOnMyOwnButton: Locator;
    readonly paywithInsuranceButton: Locator;
    readonly iHavePromoCodeButton: Locator;
    readonly afterPayBanner: Locator;
    readonly glassOnlypackagePrice: Locator;

    //Your quote is almost ready modal
    readonly skipQuoteEmailButton: Locator;
    readonly emailInput: Locator;
    readonly getMyQuoteButton: Locator;
    readonly closeButton: Locator;

    //Enter a promo code modal
    readonly promoCodeTextbox: Locator;
    readonly applyPromoButton: Locator;
    readonly repeatedClicksModalCloseButton: Locator;

    constructor(page: Page) {
        super(page);
        this.page = page;
        this.standardPackageButton = this.page.getByText('Standard');
        this.premiumPackageButton = this.page.getByText('Premium');
        this.glassOnlyButton = this.page.getByText('Glass service only', { exact: true });
        this.payOnMyOwnButton = this.page.locator('label').filter({ hasText: 'Pay on my own' });
        this.paywithInsuranceButton = this.page.locator('label').filter({ hasText: 'Pay with insurance' });
        this.iHavePromoCodeButton = this.page.getByRole('link', { name: 'I have a promo code' });
        this.skipQuoteEmailButton = this.page.getByRole('button', { name: 'Skip' });
        this.emailInput = this.page.getByRole('textbox', { name: 'Enter your email address' });
        this.getMyQuoteButton = this.page.getByRole('button', { name: 'Send' });
        this.closeButton = this.page.getByRole('dialog').locator('button').filter({ hasText: 'Close' });
        this.promoCodeTextbox = this.page.getByLabel('Enter a promo code');
        this.applyPromoButton = this.page.getByRole('button', { name: 'Apply promo code' });
        this.repeatedClicksModalCloseButton = this.page.locator('.QSISlider').locator('img[src*=\'close\']');
        this.afterPayBanner = this.page.locator('#afterpay-banner');
        this.glassOnlypackagePrice = this.page.locator('fieldset:has(legend#ServicePackageQuestion) label').filter({ hasText: 'Glass service only' }).locator('.pricing-info');
    }

    async selectPaymentMethod(method: PaymentMethod): Promise<void> {
        const locators = {
            [PaymentMethod.Insurance]: this.paywithInsuranceButton,
            [PaymentMethod.SelfPay]: this.payOnMyOwnButton
        }

        await locators[method].click();
    }

    async selectServicePackage(servicePackage: ServicePackage): Promise<void> {
        const locators = {
            [ServicePackage.GlassOnly]: this.glassOnlyButton,
            [ServicePackage.Premium]: this.premiumPackageButton,
            [ServicePackage.Standard]: this.standardPackageButton,
        }

        if (servicePackage != null) {
            await locators[servicePackage].click();
        }
    }

    async getVuex() {
        return JSON.parse(await this.page.evaluate('localStorage.getItem(\'vuex\')'));
    }

    async handleQuotePopup(email?: string): Promise<void> {
        await this.emailInput.waitFor({ state: 'visible' });
        if (await this.emailInput.isVisible()) {
            if (email) {
                await this.emailInput.fill(email);
                await this.getMyQuoteButton.click();
                if (await this.repeatedClicksModalCloseButton.isVisible()) {
                    await this.repeatedClicksModalCloseButton.click();
                }
                await this.page.waitForTimeout(3000);
                await this.closeButton.click();
            } else {
                await this.skipQuoteEmailButton.click();
            }
        }
    }

    async enterPromo(promoCode: string): Promise<void> {
        await this.iHavePromoCodeButton.click();
        await this.promoCodeTextbox.fill(promoCode);
        await this.applyPromoButton.click();
    }

    async verifyCanNotRecal(): Promise<boolean> {
        // Get Vuex state from localStorage
        const vuexState = await this.getVuex();

        // Validate in the backend to make sure the can safelite recalibrate data is correct
        if (vuexState.order?.lineItems?.glassParts?.length > 0) {
            for (const glassPart of vuexState.order.lineItems.glassParts) {
                expect(glassPart.canSafeliteRecalibrate).toBe(false);
                expect(glassPart.requiresRecalibration).toBe(true);
            }
            return true;
        }
        return false;
    }

    async verifyDynamicRecal(): Promise<void> {
        // Get Vuex state from localStorage
        const vuexState = JSON.parse(await this.page.evaluate('localStorage.getItem(\'vuex\')'));

        // Validate in the backend to make sure the dynamic recalibration part is present
        if (vuexState.order?.lineItems?.glassParts?.length > 0) {
            const hasDynamicRecalPart = vuexState.order.lineItems.glassParts.some(glassPart =>
                glassPart.childParts.some(childPart =>
                    childPart.partNumber.includes("RECAL DYNAMIC")
                )
            );

            await expect(hasDynamicRecalPart).toBe(true);
            console.log("Recal part line item is verified");
        } else {
            throw new Error("No glass parts found in the order");
        }
    }

    async verifyIsRepair(isRepair: boolean, vehicleDamage: VehicleDamage[]): Promise<void> {
        // Get Vuex state from localStorage
        const vuexState = JSON.parse(await this.page.evaluate('localStorage.getItem(\'vuex\')'));

        // Validate in the backend to make sure isRepair is aligned with the data
        if (isRepair) {
            await expect(vuexState.order.damage.isRepair).toBe(true);

            // Check the number of chips based on vehicleDamage array
            if (vehicleDamage.includes(VehicleDamage.WindshieldOneChip)) {
                await expect(vuexState.order.damage.numberOfChips).toBe(1);
            }
            if (vehicleDamage.includes(VehicleDamage.WindshieldTwoChips)) {
                await expect(vuexState.order.damage.numberOfChips).toBe(2);
            }
            if (vehicleDamage.includes(VehicleDamage.WindshieldThreeChips)) {
                await expect(vuexState.order.damage.numberOfChips).toBe(3);
            }
        } else {
            await expect(vuexState.order.damage.isRepair).toBe(false);
        }
    }


    async verifyVehicleParts(vehicleDamage: VehicleDamage[]): Promise<void> {
        // Get Vuex state from localStorage
        const vuexState = JSON.parse(await this.page.evaluate('localStorage.getItem(\'vuex\')'));

        // Validate the presence of specific parts in the glassParts array
        const glassParts = vuexState.order?.lineItems?.glassParts;

        const WINDSHIELD_TYPES = [
            "SINGLE WINDSHIELD",
            "DRIVER SPLIT WINDSHIELD",
            "PASSENGER SPLIT WINDSHIELD"
        ];

        if (glassParts?.length > 0) {
            for (const partType of vehicleDamage) {
                // Normalize part type to "WINDSHIELD" if it matches any of the defined types (Split Windshield types)
                const normalizedPartType = WINDSHIELD_TYPES.includes(partType) ? "WINDSHIELD" : partType;

                const hasPartType = glassParts.some(glassPart => glassPart.partType === normalizedPartType);

                await expect(hasPartType, `Expected part type: ${partType}`).toBe(true);
            }
        } else {
            throw new Error("No glass parts found in the order");
        }
    }

    async VerifyAfterpayBreakout() {
        const isAfterPayBannerVisible = await this.afterPayBanner.isVisible({ timeout: 2000 })
        await Soft.expect(isAfterPayBannerVisible, "AfterPay Banner is visible.").toBeTruthy();
        const localStorage = await this.getVuex();
        let isRepair = localStorage.order.damage.isRepair as boolean;

        const servicePackages = await this.page.locator('fieldset:has(legend#ServicePackageQuestion) label').filter({ visible: true }).all();
        for (const servicePackage of servicePackages) {
            const servicePackageButtonLabel = await servicePackage.getAttribute('buttonlabel');
            if (!isRepair) {
                const hasRecalPart = localStorage.order.lineItems.glassParts.find((item: any) => item.requiresRecalibration === true) ? true : false;
                const canSafeliteRecalibrate = localStorage.order.lineItems.glassParts.find((item: any) => item.canSafeliteRecalibrate === true) ? true : false;
                if (hasRecalPart && canSafeliteRecalibrate) {
                    const servicePackageTextContent = await servicePackage.textContent();
                    await Soft.expect(servicePackageTextContent, `${servicePackageButtonLabel} package contains "Expert installation and recalibration"`).toContain('Expert installation and recalibration');
                }
            }
            const afterPayPricingInfo = await servicePackage.locator('.pricing-info').filter({ visible: true }).textContent();
            const classAttribute = await this.payOnMyOwnButton.getAttribute('class');
            const isPayOnMyOwnSelected = classAttribute?.includes('selected');
            if (isPayOnMyOwnSelected) {
                const regex = /^\$\d+.\d{2}in\s4\sinterest-free\spayments\sor\s(\$\d+.\d{2}){1,2}\sin\ssingle\spayment\s$/;
                const priceInfoRegexMatch = regex.test(afterPayPricingInfo!)
                await Soft.expect(priceInfoRegexMatch, `${servicePackageButtonLabel} Package: Afterpay breakout text matches the regex`).toBeTruthy();
            } else {
                const regex = /^As\slittle\sas\s\$\d+.\d{2}$/;
                const priceInfoRegexMatch = regex.test(afterPayPricingInfo!)
                await Soft.expect(priceInfoRegexMatch, `${servicePackageButtonLabel} Package: Afterpay breakout text matches the regex`).toBeTruthy();
            }
        }
    }

    async verifyOEMPart(): Promise<void> {
        // Get Vuex state from localStorage
        const vuexState = JSON.parse(await this.page.evaluate('localStorage.getItem(\'vuex\')'));

        // Validate the presence of an OEM part
        if (vuexState.order?.lineItems?.glassParts?.length > 0) {
            const firstGlassPartNumber = vuexState.order.lineItems.glassParts[0].partNumber;

            await expect(firstGlassPartNumber.includes("OEM")).toBe(true);
        } else {
            throw new Error("No glass parts found in the order");
        }
    }

    @step("ServicePackagePage >> Select Payment Method and Service Type: ")
    async handleServicePackagePage(testData: Partial<ITestData>) {
        const { customerDetails, paymentMethod, paymentDetails, servicePackage, isCanNotRecal, isDynamicRecal, hasOemEndorsement, vehicleDamage, appointmentDetails, totalAmount } = testData;

        await this.validateProgressBar(ProgressBarPercentages.ServicePackagePage);
        // Define repair damage types (vs. replacement types)
        const repairTypes: VehicleDamage[] = [
            VehicleDamage.WindshieldOneChip,
            VehicleDamage.WindshieldTwoChips,
            VehicleDamage.WindshieldThreeChips
        ];

        // Determine if we're replacing or repairing
        const isReplace = !repairTypes.some(damageType => {
            return vehicleDamage!.includes(damageType);
        });

        await this.handleQuotePopup(customerDetails!.email!);

        // validate total amount for 3 chip repair
        if (vehicleDamage?.some(damage => damage === VehicleDamage.WindshieldThreeChips)) {
            const priceText = await this.glassOnlypackagePrice.textContent() || '$0.00';
            
            // Regex to match dollar amounts like $54.99 or $219.97
            const regex = /\$(\d+\.\d{2})/g;

            // Match all dollar amounts
            const matches = priceText.match(regex);
            const price = matches!.length > 1 ? matches![1] : matches![0];

            const actualPrice = Number.parseFloat(price.replace('$', '')).toString();
            Soft.expect(actualPrice).toEqual(totalAmount);
        }

        await this.selectPaymentMethod(paymentMethod!);
        await this.selectServicePackage(servicePackage!);

        await this.VerifyAfterpayBreakout();

        // Enter promo code
        if (paymentDetails?.promoCode) {
            await this.enterPromo(paymentDetails.promoCode);
        }

        // Backend Validations
        // Validate backend for can not recal if applicable
        if (isCanNotRecal) {
            await this.verifyCanNotRecal();
        }

        // Validate backend for dynamic recal if applicable
        if (isDynamicRecal) {
            await this.verifyDynamicRecal();
        }

        // Validate backend for repair info (including chip verification)
        await this.verifyIsRepair(!isReplace, vehicleDamage!);
        if (isReplace) {
            // Validate backend for parts info
            await this.verifyVehicleParts(vehicleDamage!);
        }

        // Validate backend for OEM endorsement
        if (hasOemEndorsement) {
            await this.verifyOEMPart();
        }

        if (appointmentDetails?.appointmentTimeSlot === AppointmentTimeslot.EarlyBird) {
            await this.mockScheduleResponseForEarlyBird(customerDetails!);
        }

        await this.nextPage();
    }
}