import PropertyUtils from "@impl/utils/PropertyUtils";
import { formatTag } from "@impl/utils/TaggingUtils";
import { Page } from "@playwright/test";
import { TestInfo } from "@business-logic/types/Test";
import { ConsoleColor } from "@business-logic/types/Enums";
import FrameworkConfig from "@business-logic/types/FrameworkConfig";
import { DisposableBase } from "@business-logic/types/IDisposable";
import ITestCase from "@business-logic/types/ITestCase";
import { ValidationOptions, ValidationResult } from "@business-logic/types/RuleEngine";
import Soft, { SoftError } from "@business-logic/validations/Soft";
import Validations from "./Validations";
import FakerUtils from "@impl/utils/FakerUtils";
import { ITestData } from "./ITestData";
import { BailoutPage } from "../../pages/BailoutPage";
import { ContactConfirmationPage } from "../../pages/ContactConfirmationPage";
import { CoverageStatementPage } from "../../pages/CoverageStatementPage";
import ITestPages from "./ITestPages";
import { DuplicateCheckPage } from "../../pages/DuplicateCheckPage";
import { OrderConfirmationPage } from "../../pages/OrderConfirmationPage";
import { PartQuestionsPage } from "../../pages/PartQuestionsPage";
import { PaymentMethodPage } from "../../pages/PaymentMethodPage";
import { PaymentPage } from "../../pages/PaymentPage";
import { PaypalPage } from "../../pages/PaypalPage";
import { PolicyHolderDetailsPage } from "../../pages/PolicyHolderDetailsPage";
import { PolicyVehiclesPage } from "../../pages/PolicyVehiclesPage";
import { ProviderPreferencePage } from "../../pages/ProviderPreferencePage";
import { SchedulePage } from "../../pages/SchedulePage";
import { ServiceLocationPage } from "../../pages/ServiceLocationPage";
import { ServicePackagesPage } from "../../pages/ServicePackagesPage";
import { TpaConfirmationPage } from "../../pages/TpaConfirmationPage";
import { TpaSearchPage } from "../../pages/TpaSearchPage";
import { TpaSubmitPage } from "../../pages/TpaSubmitPage";
import { VehicleDamagePage } from "../../pages/VehicleDamagePage";
import { VehicleLookupPage } from "../../pages/VehicleLookupPage";
import { VehicleSelectionPage } from "../../pages/VehicleSelectionPage";
import { VinLookupPage } from "../../pages/VinLookupPage";
import { WelcomePage } from "../../pages/WelcomePage";
import { ContactDetailsPage } from "../../pages/ContactDetailsPage";
import { EndorsementsPage } from "../../pages/EndorsementsPage";
import { VehicleLookupAddressPage } from "../../pages/VehicleLookupAddressPage";
import { VehicleLookupLicensePage } from "../../pages/VehicleLookupLicensePage";
import CcisApiUtil from "@impl/api/CcisApiUtil";
import MockPolicyData from "@business-logic/data/MockPolicyData";
import VehiclePartQuestionsPage from "../../pages/VehiclePartsPage";
import CapabilityQuestionsPage from "../../pages/CapabilityQuestionsPage";
import { MoldingQuestionsPage } from "../../pages/MoldingQuestionsPage";
import { AddressLookupPage } from "../../pages/AddressLookupPage";

export default class TestCase extends DisposableBase implements ITestCase {
    public static FrameworkConfig: FrameworkConfig = {
        // company: NetJetsCompanies.NJA,
        // companyString: NetJetsCompanies[NetJetsCompanies.NJA],
        // currency: CurrencyTypes.USD,
        createResources: true, //process.env.FW_CREATE_RESOURCES! === "true",
        destroyResources: true, // process.env.FW_DESTROY_RESOURCES! === "true",
        maxAllotmentHours: Number(process.env.FW_MAX_ALLOTMENT_HOURS)
    };

    public static readonly Constants = class {
        static readonly DISPOSE_HALTED: string = "FrameworkConfig is set to NOT destroy resources. Teardown halted!";
        static readonly CREATION_HALTED: string = "FrameworkConfig is set to NOT create resources. Preparation halted!";
    };

    public readonly testID?: string;
    public readonly name: string;
    public readonly tags: string[];

    public readonly testData: Partial<ITestData>;

    public readonly validations?: Validations;
    public readonly tempData?: any[] = [];


    public location: Location;
    public alternateLocation?: Location;

    public pages: ITestPages;

    public constructor(data: ITestCase, validationOptions: ValidationOptions = new ValidationOptions(), testID: string) {
        super();
        Object.assign(this, data);
        this.testID = FakerUtils.getRandomTestID();

        const validationResults: ValidationResult[] = [];

        this.name = PropertyUtils.getValue<this>(data, TestCase.name, validationResults, { isRequired: true }, x => x.name);
        this.tags = PropertyUtils.getValue<this>(data, TestCase.name, validationResults, { isRequired: true }, x => x.tags);
        this.validations = PropertyUtils.getValue<this>(data, TestCase.name, validationResults, { isRequired: false }, x => x.validations);

        // if (PropertyUtils.hasProperty<this>(data, TestCase.name, validationResults, { isRequired: false }, x => x.oldTransactions))
        //     this.oldTransactions = data.oldTransactions.map((item: any) => new TransactionData(item, validationResults, this.testID));

        // if (PropertyUtils.hasProperty<this>(data, TestCase.name, validationResults, { isRequired: true }, x => x.transaction))
        //     this.transaction = new TransactionData(data.transaction, validationResults, this.testID);

        ValidationResult.PrintValidationResults(validationResults, validationOptions);
    }

    public static getTags(testCase: ITestCase): string[] {
        let retval = [
            ...testCase.tags,
            formatTag(testCase.name),
            // formatTag(testCase.transaction.programType)
        ];

        // if (testCase.transaction.aircraft)
        //     retval.push(formatTag(testCase.transaction.aircraft.type));

        // testCase.oldTransactions.forEach((t) => {
        //     retval.push(formatTag(t.programType));
        //     if (t.aircraft)
        //         retval.push(formatTag(t.aircraft.type));
        // });
        return retval;
    }

    public static async afterEachMethod(page: Page, testInfo: TestInfo) {
        const originalStatus = testInfo.status;

        if (Soft.hasFailedAssertions())
            testInfo.status = "failed";

        for (let a of Soft.getFailedAssertions())
            testInfo.errors.push(new SoftError(a));

        // NOTE: This try catch is here because the tests when locally, frequently
        //        fail tests on screenshot, which we do not want, is it make it
        //        harder to parse any other real errors we do care about. T.S. 9.5.2024
        try {
            await testInfo.attach("End of Test Screenshot", {
                body: await page.screenshot({ fullPage: true }),
                contentType: 'image/png'
            });
        } catch (error) {
            console.error(error);
        }


        if (testInfo.testCase)
            if (!(testInfo.testCase.testData!.isMockTesting ?? false)) {
                if (testInfo.testCase.testData?.isPolicyFound) {
                    await testInfo.testCase.disposeAll();
                }
            }

        const seconds: string = String(testInfo.duration / 1000);
        const minutes: string = (testInfo.duration / 1000 / 60).toFixed(2);
        const validationErrors: string = Soft.hasFailedAssertions() ? ` with ${Soft.getFailureCount()} validation errors` : "";

        if (originalStatus == "failed")
            console.log(`${ConsoleColor.Red}Test failed after ${seconds} seconds, or roughly ${minutes} minutes${validationErrors}.${ConsoleColor.Reset}`);
        else {
            if (testInfo.status == "passed")
                console.log(`${ConsoleColor.Green}Test finished successfully in ${seconds} seconds, or roughly ${minutes} minutes.${ConsoleColor.Reset}`);
            else
                console.log(`${ConsoleColor.Orange}Test finished in ${seconds} seconds, or roughly ${minutes} minutes${validationErrors}.${ConsoleColor.Reset}`);
        }
        console.log(page.url());
    }

    public async setup(): Promise<void> {
        if (this.testData.policySoap && this.testData.claimDetails) {
            const apiUtil = new CcisApiUtil();

            // Create Policy
            const req = MockPolicyData.generateCreateMockPolicyRequest(this.testData.claimDetails.policyNumber, this.testData.policySoap);
            console.log('Policy Number: ' + this.testData.claimDetails.policyNumber + '   Damage Date: ' + this.testData.claimDetails.damageDate + '   Postal code: ' + this.testData.customerDetails?.address.postalCode!);
            const res = await apiUtil.createFakeResponse(req);
            console.log("Fake Policy creation status" + res.status); // For debug use console.dir(res);

            // Create Claim Registration
            const claimRegReq = MockPolicyData.generateCreateClaimRegistrationRequest(this.testData.claimDetails.policyNumber);
            const claimRegRes = await apiUtil.createFakeResponse(claimRegReq);
            console.log("Fake claim registration creation status: " + claimRegRes.status);  //For debug use console.dir(claimRegRes);
        }
    }

    public setupPages(page: Page): void {
        this.pages = {
            bailoutPage: new BailoutPage(page),
            capabilityQuestionsPage: new CapabilityQuestionsPage(page),
            contactConfirmationPage: new ContactConfirmationPage(page),
            contactDetailsPage: new ContactDetailsPage(page),
            coverageStatementPage: new CoverageStatementPage(page),
            duplicateCheckPage: new DuplicateCheckPage(page),
            endorsementsPage: new EndorsementsPage(page),
            orderConfirmationPage: new OrderConfirmationPage(page),
            partQuestionsPage: new PartQuestionsPage(page),
            paymentMethodPage: new PaymentMethodPage(page),
            paymentPage: new PaymentPage(page),
            paypalPage: new PaypalPage(page),
            policyHolderDetailsPage: new PolicyHolderDetailsPage(page),
            policyVehiclesPage: new PolicyVehiclesPage(page),
            providerPreferencePage: new ProviderPreferencePage(page),
            schedulePage: new SchedulePage(page),
            serviceLocationPage: new ServiceLocationPage(page),
            servicePackagesPage: new ServicePackagesPage(page),
            tpaConfirmationPage: new TpaConfirmationPage(page),
            tpaSearchPage: new TpaSearchPage(page),
            tpaSubmitPage: new TpaSubmitPage(page),
            vehicleDamagePage: new VehicleDamagePage(page),
            vehicleLookupPage: new VehicleLookupPage(page),
            vehiclePartQuestionsPage: new VehiclePartQuestionsPage(page),
            vehicleSelectionPage: new VehicleSelectionPage(page),
            vinLookupPage: new VinLookupPage(page),
            vehicleLookupAddressPage: new VehicleLookupAddressPage(page),
            vehicleLookupLicensePage: new VehicleLookupLicensePage(page),
            welcomePage: new WelcomePage(page),
            moldingQuestionsPage: new MoldingQuestionsPage(page),
            addressLookupPage: new AddressLookupPage(page)
        };
    }

    protected async dispose(): Promise<void> {
        if (this.testData.policySoap && this.testData.claimDetails) {
            const apiUtil = new CcisApiUtil();

            // Delete policy
            const policyDeleteRes = await apiUtil.deleteFakeResponse({
                accountNumber: '550036', // TODO: Change when we have more clients
                key: this.testData.claimDetails.policyNumber,
                responseType: 'Policy'
            });
            console.log("Delete 'Fake policy' status: " + policyDeleteRes.status); //For debug use console.dir(policyDeleteRes);

            // Delete Claim Registration
            const crDeleteRes = await apiUtil.deleteFakeResponse({
                accountNumber: '550036', // TODO: Change when we have more clients
                key: this.testData.claimDetails.policyNumber,
                responseType: 'ClaimRegistration'
            });
            console.log("Delete 'Fake Claim Registration policy' status: " + crDeleteRes.status); //For debug use console.dir(crDeleteRes);
        }
    }
}
