import { Page } from "@playwright/test";
import { addSmokeTagToRandomTest, Flow, ServiceLocation } from 'safelite-playwright-core';
import { ValidationOptions } from 'safelite-playwright-core';
import heavyTruckTests from "./alert-validation/alert0001_HeavyTruck";
import repairAndReplaceTests from "./alert-validation/alert0002_RepairAndReplace";
import splitWindshieldTests from "./alert-validation/alert0003_SplitWindshield";
import repairOnlyTests from "./alert-validation/alert0004_RepairOnly";
import unserviceableZipTests from "./alert-validation/alert0005_UnserviceableZip";
import invalidZipTests from "./alert-validation/alert0006_InvalidZip";
import vinNotFoundTests from "./alert-validation/alert0007_VinNotFound";
import { TestSuccessAlert } from 'safelite-playwright-core';
import { VehicleLookupType, VehicleDamage, PaymentType } from 'safelite-playwright-core';
import { PaymentMethod } from "framework/localTypes/Enums";
import cashRepairMobileCCTests from "./CashRepairMobileCreditCard";
import cashReplaceDynamicRecalMobileTests from "./CashReplaceDynamicRecalMobile";
import cashReplaceGlassAddressLookupInshopAfterPayTests from "./CashReplaceGlassAddressLookupInshopAfterPay";
import cashReplaceGlassLicensePlateLookupInshopPaypalTests from "./CashReplaceGlassLicensePlateLookupInshopPaypal";
import { ApiResponseInterceptUtil } from 'safelite-playwright-core';
import cashReplaceGlassPromoInshopTests from "./CashReplaceGlassPromoInshop";
import cashReplaceMultiGlassPromoInshopTests from "./CashReplaceMultiGlassPromoInshop";
import cashReplaceRainDefensePromoInshopTests from "./CashReplaceRainDefensePromoInshop";
import cashReplaceSafeliteCanNotRecalMobileTests from "./CashReplaceSafeliteCanNotRecalMobile";
import cashReplaceVinMobileTests from "./CashReplaceVinMobile";
import cashReplaceWiperDropoffTests from "./CashReplaceWiperDropoff";
import cashReplaceWiperPromoInShopTests from "./CashReplaceWiperPromoInshop";
import insuranceAcuityPaypalTests from "./InsuranceAcuityPaypal";
import insuranceITAC21stCenturyTests from "./InsuranceITAC21stCentury";
import insuranceGeicoTests from "./InsuranceGeico";
import insuranceITACOptimizedPriceValidationAllStateTests from "./InsuranceITACOptimizedPriceValidationAllState";
import insuranceNoCompProgressiveTests from "./InsuranceNoCompProgressive";
import cashRepairInShopAfterPayTests from "./CashRepairInShopAfterPay";
import cashRepairInShopPayPalTests from "./CashRepairInShopPayPal";
import cashReplaceMultiSlidingGlassDropoffTests from "./CashReplaceMultiSlidingGlassDropoff";
import cashReplaceMultiGlassMobileTests from "./CashReplaceMultiGlassMobile";
import { getTestObject, TestCase, prepareTest, RuleEngine, TestInfo } from 'framework/Typedefs';
import { createTestPages } from "framework/TestPages";
import cashReplaceSwitchToInsuranceProgressiveNoCompTests from "./CashReplaceSwitchToInsuranceProgressiveNoComp";
import insuranceBigTruckVerifiedTests from "./InsuranceBigTruckVerified";
import insuranceUnverifiedTests from "./InsuranceUnverified";
import CashReplaceSplitWindshieldTests from "./CashReplaceSplitWindshield";
import insuranceMeemicNearSchoolVerifiedTests from "./InsuranceMeemicNearSchoolVerified";

const test = getTestObject();

/**
 * Master Test Runner
 * 
 * This file orchestrates the execution of all test scenarios and defines the primary test workflow.
 * It imports all test cases and executes them through a common test runner function to ensure 
 * consistent behavior across different test scenarios.
 */

// Initialize shared rule engine and validation options
const ruleEngine = new RuleEngine();
const options = new ValidationOptions();

// Add smoke tag to selected tests for CI/CD pipelines
addSmokeTagToRandomTest(splitWindshieldTests);


// Standard test scenarios
const allStandardTests = [
    {name: "CashRepairMobileCreditCard", tests: cashRepairMobileCCTests},
    {name: "CashRepairInShopAfterPay", tests: cashRepairInShopAfterPayTests},
    {name: "CashRepairInShopPayPal", tests: cashRepairInShopPayPalTests},
    {name: "CashReplaceDynamicRecalMobile", tests: cashReplaceDynamicRecalMobileTests}, 
    {name: "CashReplaceGlassAddressLookupInshopAfterPay", tests: cashReplaceGlassAddressLookupInshopAfterPayTests},
    {name: "CashReplaceGlassLicensePlateLookupInshopPaypal", tests: cashReplaceGlassLicensePlateLookupInshopPaypalTests},
    {name: "CashReplaceGlassPromoInShop", tests: cashReplaceGlassPromoInshopTests},
    {name: "CashReplaceMultiGlassPromoInShop", tests: cashReplaceMultiGlassPromoInshopTests},
    {name: "CashReplaceMultiSlidingGlassDropoff",tests: cashReplaceMultiSlidingGlassDropoffTests},
    {name: "CashReplaceMultiGlassMobile",tests: cashReplaceMultiGlassMobileTests},
    {name: "CashReplaceRainDefensePromoInshop", tests: cashReplaceRainDefensePromoInshopTests},
    {name: "CashReplaceSafeliteCanNotRecalMobile", tests: cashReplaceSafeliteCanNotRecalMobileTests},
    {name: "CashReplaceVinMobile", tests: cashReplaceVinMobileTests},
    {name: "CashReplaceWiperDropoff", tests: cashReplaceWiperDropoffTests},
    {name: "CashReplaceWiperPromoInshop", tests: cashReplaceWiperPromoInShopTests},
    {name: "CashReplaceSwitchToInsuranceProgressiveNoComp", tests: cashReplaceSwitchToInsuranceProgressiveNoCompTests},
    // TODO: Uncomment when QA is ready to run heavy truck tests
    // {name: "CashReplaceSplitWindshield", tests: CashReplaceSplitWindshieldTests},
    {name: "InsuranceAcuityPaypal", tests: insuranceAcuityPaypalTests},
    {name: "InsuranceITAC21stCentury", tests: insuranceITAC21stCenturyTests},
    {name: "InsuranceNoCompProgressive", tests: insuranceNoCompProgressiveTests},
    // {name: "InsuranceBigTruckVerified", tests: insuranceBigTruckVerifiedTests},
    {name: "InsuranceUnverified", tests: insuranceUnverifiedTests},
    // {name: "InsuranceGeico", tests: insuranceGeicoTests},
    // {name: "InsuranceITACOptimizedPriceValidationAllState", tests: insuranceITACOptimizedPriceValidationAllStateTests}
    {name: "InsuranceMeemicNearSchoolVerified", tests: insuranceMeemicNearSchoolVerifiedTests},

];

// Alert validation scenarios
const allAlertTests = [
    //TODO: Uncomment when QA is ready to run heavy truck tests
    // { name: "Alert Scenario 1: Heavy Truck", tests: heavyTruckTests },
    { name: "Alert Scenario 2: Repair and Replace", tests: repairAndReplaceTests },
    // { name: "Alert Scenario 3: Split Windshield", tests: splitWindshieldTests },
    { name: "Alert Scenario 4: Repair Only", tests: repairOnlyTests },
    { name: "Alert Scenario 5: Unserviceable Zip", tests: unserviceableZipTests },
    { name: "Alert Scenario 6: Invalid Zip", tests: invalidZipTests },
    { name: "Alert Scenario 7: VIN Not Found", tests: vinNotFoundTests }
];

// Standard test cases
test.describe.parallel('Standard E2E Test Flows', () => {
    allStandardTests.forEach(scenario => {
        scenario.tests.forEach(testCase => {
            test(...prepareTest(testCase, createTestPages, run, options, ruleEngine));
        });
    });
});

// Alert validation test cases
test.describe.parallel('Alert Validation Tests', () => {
    allAlertTests.forEach(scenario => {
        test.describe(scenario.name, () => {
            scenario.tests.forEach(testCase => {
                test(...prepareTest(testCase, createTestPages, run, options, ruleEngine));
            });
        });
    });
});


/**
 * After each test, capture screenshot and handle cleanup
 */
test.afterEach(async ({ page, testInfo }) => {
    await TestCase.afterEachMethod(page, testInfo);
});

/**
 * Main test runner function that executes each test case
 * @param page - The Playwright page object
 * @param testInfo - Test information and context
 */
async function run(page: Page, testInfo: TestInfo): Promise<void> {
    try {
        await testInfo.testCase.setup();
        testInfo.testCase.setupPages(page, createTestPages);
        await testInfo.testCase.pages.homePage.goto();
        await runWorkflow(page, testInfo.testCase);
    } catch (error) {
        // Catch successful alert tests and log message
        if (error instanceof TestSuccessAlert) {
            console.log(error.message);
        // Catch and throw other errors
        } else {
            throw error;
        }
    }
}

/**
 * Main workflow that executes the test steps in sequence
 * 
 * This function contains the common test flow for all test scenarios:
 * 1. Start at home page and enter vehicle/damage information
 * 2. Select lookup method and provide details
 * 3. Answer part questions if applicable
 * 4. Select service package and payment method
 * 5. Handle insurance flow if applicable
 * 6. Select service location and schedule appointment
 * 7. Complete order and validate confirmation
 * 
 * @param page - The Playwright page object
 * @param testCase - The test case to execute
 */
async function runWorkflow(page: Page, testCase: TestCase) {

    // Intercept API Responses
    const apiResponseInterceptUtil = new ApiResponseInterceptUtil(testCase.testData);
    page.on('response', apiResponseInterceptUtil.handleInterceptResponse);

    // Destructure test data for easier access
    const { 
        paymentMethod, customerDetails, vehicleDetails, vehicleDamage, 
        paymentDetails, partQuestions, isEnterFunnelWithZip, capabilityQuestions, 
        vehiclePartQuestions, moldingQuestions, isSkipEstimatePage
     } = testCase.testData;

    // Check if the vehicle damage includes a windshield crack
    const hasWindshieldCrack = vehicleDamage!.some(damage =>
        damage === VehicleDamage.WindshieldCrack ||
        damage === VehicleDamage.DriverSplitWindshield ||
        damage === VehicleDamage.PassengerSplitWindshield ||
        damage === VehicleDamage.SingleSplitWindshield
    );


    //============================= TEST WORKFLOW STEPS =============================

    // Use Environment Variable to decide whether or not we want to skip content site aka home page
    if (process.env.SKIP_CONTENT_SITE == "false") {     
        await test.step('HomePage >> Lets Get Started', async () => {
            let homePage = testCase.pages.homePage;
            console.log(`Customer for this test: ${customerDetails?.firstName} ${customerDetails?.lastName}`);
            await homePage.letsGetStarted(customerDetails?.address.postalCode!, !!isEnterFunnelWithZip!);
        });
    } else {
        // If enterFunnelWithZip is true, add zip code to url
        if (!!isEnterFunnelWithZip!) {
            const currentUrl = process.env['BASE_URL']!;
            const zipParam = `&zipCode=${customerDetails?.address.postalCode!}`;
            if (!currentUrl.includes('zipCode=')) {
                await page.goto(currentUrl + zipParam);
            }
        }
        console.log(`Customer for this test: ${customerDetails?.firstName} ${customerDetails?.lastName}`);
    }

    // Handle vehicle selection page
    let vehicleSelectionPage = testCase.pages.vehicleSelectionPage;
    await vehicleSelectionPage.handleVehicleSelectionPage(testCase.testData);

    // Handle vehicle damage page
    let vehicleDamagePage = testCase.pages.vehicleDamagePage;
    await vehicleDamagePage.handleVehicleDamagePage(testCase.testData);

    // If the vehicle has a windshield crack as part of its damage, go to estimate page and select lookup type
    if (hasWindshieldCrack && !isSkipEstimatePage) {
        let estimatePage = testCase.pages.estimatePage;
        await estimatePage.handleEstimatePage(testCase.testData);
    
        // Handle different vehicle lookup methods
        switch (vehicleDetails!.vehicleLookupType!) {
            case VehicleLookupType.Address:
                let vehicleLookupAddressPage = testCase.pages.vehicleLookupAddressPage;
                await vehicleLookupAddressPage.handleVehicleLookupAddressPage(testCase.testData);
                break;
                
            case VehicleLookupType.LicensePlateNumber:
                let vehicleLookupLicensePage = testCase.pages.vehicleLookupLicensePage;
                await vehicleLookupLicensePage.handleVehicleLookupLicensePage(testCase.testData);
                break;
                
            case VehicleLookupType.Vin:
                let vinLookupPage = testCase.pages.vinLookupPage;
                await vinLookupPage.handleVehicleLookupVinPage(testCase.testData);
                break;
                
            case VehicleLookupType.Zip:
                let serviceZipPage = testCase.pages.serviceZipPage;
                await serviceZipPage.handleServiceZipPage(testCase.testData);
                break;
        }
    } else {
        // Otherwise just use zip lookup for service zip page
        let serviceZipPage = testCase.pages.serviceZipPage;
        await serviceZipPage.handleServiceZipPage(testCase.testData);
    }

    // Handle part questions if applicable
    if (partQuestions && partQuestions.length > 0) {
        let partQuestionsPage = testCase.pages.partQuestionsPage;
        await partQuestionsPage.handlePartQuestionsPage(testCase.testData);
    }
    
    // Handle molding questions if applicable
    if (moldingQuestions && moldingQuestions.length > 0) {
        let moldingQuestionsPage = testCase.pages.moldingQuestionsPage;
        await moldingQuestionsPage.handleMoldingQuestionsPage(testCase.testData);
    }

    // Handle vehicle part questions if applicable
    if (vehiclePartQuestions && vehiclePartQuestions.length > 0) {
        let vehiclePartsPage = testCase.pages.vehiclePartsPage;
        await vehiclePartsPage.handleVehiclePartsPage(testCase.testData);
    }

    // Handle capability questions if applicable
    if (capabilityQuestions && capabilityQuestions.length > 0) {
        let capabilityQuestionsPage = testCase.pages.capabilityQuestionsPage;
        await capabilityQuestionsPage.handleCapabilityQuestionsPage(testCase.testData);
    }

    // Select service package and payment method
    let servicePackagesPage = testCase.pages.servicePackagesPage;
    await servicePackagesPage.handleServicePackagePage(testCase.testData);

    //============================= INSURANCE FLOW =============================`   
    
    // Insurance flow - if user selected Insurance as Payment Method
    if (paymentMethod == PaymentMethod.Insurance) {
        await handleInsuranceFlow(testCase);
    }

    //============================= SERVICE SCHEDULING =============================
    
    // Select service location
    // let serviceLocationPage = testCase.pages.serviceLocationPage;
    // await serviceLocationPage.handleServiceLocationPage(testCase.testData);

    // Schedule appointment
    let schedulePage = testCase.pages.schedulePage;
    await schedulePage.handleSchedulePage(testCase.testData);

    if (testCase.testData.appointmentDetails?.serviceLocation === ServiceLocation.Mobile) {
        let mobileDetailsPage = testCase.pages.mobileDetailsPage;
        await mobileDetailsPage.handleMobileDetailsPage(testCase.testData);
    }

    // Enter contact details
    let contactDetailsPage = testCase.pages.contactDetailsPage;
    await contactDetailsPage.handleContactDetailsPage(testCase.testData);

    //============================= PAYMENT PROCESSING =============================
    
    // Handle payment
    let paymentMethodPage = testCase.pages.paymentMethodPage;
    await paymentMethodPage.handlePaymentMethodPage(testCase.testData);

    // If user selected Pay with Insurance as Payment Method, Enter Insurance Flow
    if (paymentDetails?.paymentType === PaymentType.PayWithInsurance) {
        await handleInsuranceFlow(testCase);
    }

    //============================= ORDER CONFIRMATION =============================
    
    // Validate order confirmation
    let orderConfirmationPage = testCase.pages.orderConfirmationPage;
    await orderConfirmationPage.verifyOrderConfirmationPage(testCase.testData);
}

export async function handleInsuranceFlow(testCase: TestCase) {
    const { isPolicyFound, isPolicyDriver, endorsements, isRecalVehicle, isCashInsuranceFlow, flow } = testCase.testData;
        
        // Check if the insurance policy has endorsements
        const hasEndorsements = endorsements && endorsements.length > 0;

        // Handle insurance company page
        let insuranceCompanyPage = testCase.pages.insuranceCompanyPage;
        await insuranceCompanyPage.handleInsuranceCompanyPage(testCase.testData);

        // Handle ccPolicyInfoPage
        let ccPolicyInfoPage = testCase.pages.ccPolicyInfoPage;
        await ccPolicyInfoPage.handleCCPolicyInfoPage(testCase.testData);

        let duplicateCheckPage = testCase.pages.duplicateCheckPage;
        if (duplicateCheckPage.page.url().includes('DuplicateCheck.aspx')) {
            await duplicateCheckPage.handleDuplicateCheckPage(testCase.testData);
        }

        if ( flow === Flow.Managed ) {
            let policyVehiclesPage = testCase.pages.policyVehiclesPage;
            await policyVehiclesPage.handlePolicyVehiclesPage(testCase.testData);

            // Handle policy driver selection if applicable
            if (isPolicyDriver) {
                let policyDriverPage = testCase.pages.policyDriverPage;
                await policyDriverPage.handlePolicyDriverPage(testCase.testData);
            }

            // Handle endorsements if applicable
            if (hasEndorsements) {
                let endorsementsPage = testCase.pages.endorsementsPage;
                await endorsementsPage.handleEndorsementsPage(testCase.testData);
            }
        } else {
            
            //Handle verify details page
            let verifyDetailsPage = testCase.pages.verifyDetailsPage;
            await verifyDetailsPage.handleVerifyDetailsPage(testCase.testData);
        }

        // Handle Policy info submitted page
        let policyInfoSubmittedPage = testCase.pages.policyInfoSubmittedPage;
        await policyInfoSubmittedPage.handlePolicyInfoSubmittedPage();

        if(isRecalVehicle)
        {
            let recalibrationInfoPage = testCase.pages.recalibrationInfoPage;
            await recalibrationInfoPage.handleRecalibrationInfoPage();
        }

        //handle coveraage statement page
        let coverageStatementPage = testCase.pages.coverageStatementPage;
        await coverageStatementPage.handleCoverageStatementPage(testCase.testData);

        // Handle scenario where user selected Cash to Insurance and needs to go back through the flow
        // right now we are choosing pay at appointment as payment method for this scenario
        if (isCashInsuranceFlow) {
            // Schedule appointment
            let schedulePage = testCase.pages.schedulePage;
            await schedulePage.handleSchedulePage(testCase.testData);

            if (testCase.testData.appointmentDetails?.serviceLocation === ServiceLocation.Mobile) {
                let mobileDetailsPage = testCase.pages.mobileDetailsPage;
                await mobileDetailsPage.handleMobileDetailsPage(testCase.testData);
        }
            //customer dertails
            let contactDetailsPage = testCase.pages.contactDetailsPage;
            await contactDetailsPage.nextPage();

            //paymentmethods
            let paymentMethodPage = testCase.pages.paymentMethodPage;
            await paymentMethodPage.nextPage();
        }
}