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

const test = getTestObject();

export class OrderConfirmationPage extends BasePage {
    readonly page: Page;
    readonly serviceText: Locator;
    readonly emailText: Locator;
    readonly apptDateText: Locator;
    readonly amountDueText: Locator;
    readonly viewCartButton: Locator;
    readonly deductibleText: Locator;
    readonly subtotalText: Locator;
    readonly finalAmountDue: Locator;
    readonly cartServicePackageText: Locator;
    readonly winshieldWiper: Locator;
    readonly rainDefense: Locator;
    readonly appointmentSummarySection: Locator;
    readonly orderDetailsSection: Locator;
    readonly amountPaid: Locator;

    constructor(page: Page) {
        super(page);
        this.page = page;
        this.serviceText = this.page.locator('p', {
            hasText: /to service your/
        });
        this.emailText = this.emailText = this.page.getByText('A confirmation email was sent');
        this.apptDateText = this.page.locator('[class="scheduleText"]');
        this.amountDueText = this.page.getByLabel('expand cart');
        this.viewCartButton = this.page.locator('#cart-dropdown-head');
        this.deductibleText = this.page.locator('.deductible');
        this.subtotalText = this.page.locator('.sub-total');
        this.finalAmountDue = this.page.locator('div.amount-due');
        this.cartServicePackageText = this.cartServicePackageText = this.page.locator('.cart-panel');
        this.appointmentSummarySection = this.page.locator('.main .scheduleText, .main .add-to-calendar-text, .main .appointment-text, .main .duration-text-block');
        this.orderDetailsSection = this.page.locator('.cart .vin-toggle a');
        this.amountPaid = this.page.locator('.amount-paid span').nth(1);
    }

    async validateOrderConfirmationPage(testData: Partial<ITestData>) {
        // Destructure data we use
        const { vehicleDetails, customerDetails, servicePackage, isCashInsuranceFlow,
            isPolicyFound, claimDetails, paymentDetails, isUseVehicleOnPolicy, paymentMethod, isPolicyUnverified } = testData;
        await this.serviceText.waitFor({ state: "visible" });

        Soft.expect((await this.getActualAppointmentSummary()).map(item => item.toLowerCase())).toEqual((await this.getExpectedAppointmentSummary(testData)).map(item => item.toLowerCase()));

        // Grab text
        const emailTextValue = await this.emailText.textContent();
        const servicePackageValue = await this.cartServicePackageText.textContent();
        const amountDueValue = await this.amountDueText.textContent();
        // const deductibleTextValue = await this.deductibleText.textContent();
        const subtotalTextValue = await this.subtotalText.textContent();
        const finalAmountDueValue = await this.finalAmountDue.textContent();

        // Extract service package price
        const servicePackageAmt = Number.parseFloat(servicePackageValue!.split('$')[1].replaceAll(',', ''));

        // General Validations
        Soft.expect(emailTextValue?.toLowerCase()).toContain(customerDetails!.email);

        // Service package validations
        await Soft.expect(servicePackageValue).toContain(`${servicePackage}`)
        if (servicePackage === ServicePackage.Premium || servicePackage === ServicePackage.Standard) {
            Soft.expect(servicePackageValue).toContain('New wiper blades');
        }
        if (servicePackage === ServicePackage.Premium) {
            Soft.expect(servicePackageValue).toContain('Rain Repel Treatment');
        }

        // Promo Code Validation
        if (paymentDetails!.promoCode) {
            expect.soft(servicePackageValue).toContain(`Promo code ${paymentDetails!.promoCode} applied`);
        };

        if (paymentMethod === PaymentMethod.SelfPay || (paymentDetails?.paymentType && paymentDetails?.paymentType !== PaymentType.PayWithInsurance)) {
            // Cart validation for non insurance users 
            // Extract numbers
            const amountDueAmt = Number.parseFloat(amountDueValue!.split('$')[1].replaceAll(',', ''));
            // const deductibleAmt = deductibleTextValue? Number.parseFloat(deductibleTextValue.split('$')[1].replaceAll(',', '')): 0;
            const subtotalAmt = Number.parseFloat(subtotalTextValue!.split('$')[1].replaceAll(',', ''));
            subtotalTextValue?.replaceAll(',', '')
            const finalAmountDueAmt = Number.parseFloat(finalAmountDueValue!.split('$')[1].replaceAll(',', ''));

            Soft.expect(subtotalAmt).toBeGreaterThan(0);
            // Soft.expect(deductibleAmt).toEqual(0);

            if ((paymentDetails!.paymentType === PaymentType.PayAtService || isCashInsuranceFlow) && (servicePackageAmt > 0)) {
                // Verify amount due > 0
                Soft.expect(amountDueAmt).toBeGreaterThan(0);
                Soft.expect(finalAmountDueAmt).toBeGreaterThan(0);

                if (isPolicyUnverified && PaymentType.PayWithInsurance) {
                    Soft.expect(finalAmountDueAmt).toContain('Verifying coverage')
                }
            } else {
                // Verify amount due 0
                Soft.expect(amountDueAmt).toEqual(0);
                Soft.expect(finalAmountDueAmt).toEqual(0);
            }
        }
    }

    async getFormattedAppointmentDate(appointmentDate: string) {

        // Parse the original date
        let parsedAppointmentDate = new Date(`${appointmentDate}` + 'T00:00:00');

        // Format the new date as a string
        let updatedAppointmentDate = parsedAppointmentDate.toLocaleDateString('en-US', { weekday: 'long', month: 'long', day: 'numeric' });
        return updatedAppointmentDate;
    }

    async toggleOrderDetailsSection() {
        await this.orderDetailsSection.click().then(
            async () => {
                await waitUntil(async () => {
                    return (await this.page.locator('.vin-toggle.expanded').evaluate(el => window.getComputedStyle(el, ':after').transform)) === 'matrix(-1, 0, 0, -1, 0, 0)';
                });
            }
        );
    }

    async logOrderNumber() {
        const sessionStorage = JSON.parse(await this.page.evaluate('sessionStorage.getItem(\'submittedState\')'));
        return sessionStorage.order.workOrderNumber;
    }

    async validateSettledAmountForPia() {
        const sessionStorage = JSON.parse(await this.page.evaluate('sessionStorage.getItem(\'submittedState\')'));
        const isPia = sessionStorage.order.payment.isPia;
        if (isPia) {
            const nextGenSettledAmount = Number.parseFloat(sessionStorage.order.payment.nextGenSettledAmount);
            Soft.expect(nextGenSettledAmount, `NextGen Settled Amount > 0. NextGenSettledAmount: ${nextGenSettledAmount}`).toBeGreaterThan(0);
            await this.toggleOrderDetailsSection();
            let amountPaid = await this.amountPaid.textContent();
            const parsedAmount = amountPaid === null 
                ? console.log("Amount paid is null. Check the locator.")
                : Number.parseFloat(amountPaid.replace(/[^0-9.]/g, ''));
            Soft.expect(parsedAmount).toEqual(nextGenSettledAmount);
        }
    }

    async getActualAppointmentSummary(): Promise<string[]> {
        let appointmentSummary: string[] = [];
        for (let element of await this.appointmentSummarySection.all()) {
            let text = (await element.textContent() || "").replaceAll(/\u00A0|&nbsp;/g, ' ');
            appointmentSummary.push(text.trim());
        }
        return appointmentSummary;
    }

    async getExpectedAppointmentSummary(testData: Partial<ITestData>): Promise<string[]> {
        const { appointmentDetails, customerDetails, vehicleDetails } = testData;
        let appointmentSummary: string[] = [];

        // Format the expected appointment date
        const formattedExpectedAppointmentDate = await this.getFormattedAppointmentDate(customerDetails!.apptDate!);
        appointmentSummary.push(
            appointmentDetails?.serviceLocation == ServiceLocation.Mobile
                ? formattedExpectedAppointmentDate + `${customerDetails!.apptTime?.replace("arriving between", "Between").replaceAll(":00", "")}`
                : appointmentDetails?.serviceLocation == ServiceLocation.InShop
                    ? formattedExpectedAppointmentDate + `${customerDetails!.apptTime}`
                    : formattedExpectedAppointmentDate + "Drop off before 9:30 AM"
                    // : formattedExpectedAppointmentDate + "before 9:30 AM"
        );
        appointmentSummary.push("Add to calendar");
        appointmentSummary.push(    
            appointmentDetails?.serviceLocation == ServiceLocation.Mobile
                ? appointmentDetails?.serviceAddress
                    ? ("We're coming to you at" + appointmentDetails.serviceAddress.street + ", " + appointmentDetails.serviceAddress.city + ", " + appointmentDetails.serviceAddress.state + " " + appointmentDetails.serviceAddress.postalCode + "to service your " + `${vehicleDetails!.year} ${vehicleDetails!.make} ${vehicleDetails!.model}`)
                    : ""
                : appointmentDetails?.shopAddress
                    ? ("You're going to a Safelite shop at" + appointmentDetails.shopAddress + "to service your " + `${vehicleDetails!.year} ${vehicleDetails!.make} ${vehicleDetails!.model}`)
                    : "");
        appointmentSummary.push("Duration: " + customerDetails!.apptDuration!);

        return appointmentSummary;
    }

    @step("OrderConfirmationPage >>  Validate order")
    async verifyOrderConfirmationPage(testData: Partial<ITestData>) {
        await this.page.waitForURL(new RegExp('(.+)confirmation'), { timeout: 60000 });
        await this.validateProgressBar(ProgressBarPercentages.OrderConfirmationPage);
        await this.validateOrderConfirmationPage(testData);
        const workOrderNumber = await this.logOrderNumber();
        await this.validateSettledAmountForPia();
        await test.step(`Session Storage Work Order Number: ${workOrderNumber}`, async () => {
            console.log(`Session Storage Work Order Number: ${workOrderNumber}`);
        });
    }
}