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 ITestPages from "./ITestPages";
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 { HomePage } from "../../pages/HomePage";
import { VehicleSelectionPage } from "../../pages/VehicleSelectionPage";
import { VehicleDamagePage } from "../../pages/VehicleDamagePage";
import { EstimatePage } from "../../pages/EstimatePage";
import { VehicleLookupAddressPage } from "../../pages/VehicleLookupAddressPage";
import { VehicleLookupLicensePage } from "../../pages/VehicleLookupLicensePage";
import { VinLookupPage } from "../../pages/VinLookupPage";
import { ServicePackagesPage } from "../../pages/ServicePackagesPage";
import { LeadgenHomePage } from "../../pages/LeadgenHomePage";
import { ServiceLocationPage } from "../../pages/ServiceLocationPage";
import { SchedulePage } from "../../pages/SchedulePage";
import { ContactDetailsPage } from "../../pages/ContactDetailsPage";
import { PaymentMethodPage } from "../../pages/PaymentMethodPage";
import { ZipLookupPage } from "../../pages/ZipLookupPage";
import { OrderConfirmationPage } from "../../pages/OrderConfirmationPage";
import { PartQuestionsPage } from "../../pages/PartQuestionPage";
import VehiclePartQuestionsPage from "../../pages/VehiclePartsPage";
import CapabilityQuestionsPage from "../../pages/CapabilityQuestionsPage";
import MoldingQuestionsPage from "../../pages/MoldingQuestionsPage";
import { InsuranceCompanyPage } from "../../pages/InsuranceCompanyPage";
import { CCPolicyInfoPage } from "../../pages/CCPolicyInfoPage";
import { DuplicateCheckPage } from "../../pages/DuplicateCheckPage";
import { PolicyVehiclesPage } from "../../pages/PolicyVehiclesPage";
import { PolicyInfoSubmittedPage } from "../../pages/PolicyInfoSubmittedPage";
import RecalibrationInfoPage from "../../pages/RecalibrationInfoPage";
import { CoverageStatementPage } from "../../pages/CoverageStatementPage";
import { VerifyDetailsPage } from "../../pages/VerifyDetailsPage";
import { EndorsementsPage } from "../../pages/EndorsementsPage";
import { PolicyDriverPage } from "../../pages/PolicyDriverPage";

// File for Test Case Class

export default class TestCase extends DisposableBase implements ITestCase {
    public static FrameworkConfig: FrameworkConfig = {
        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[] {
        const retval = [
            ...testCase.tags,
            formatTag(testCase.name),
        ];

        return retval;
    }

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

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

        for (const 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)
            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> {
        //Setup for Test case
    }

    public setupPages(page: Page): void {
        this.pages = {
            capabilityQuestionsPage: new CapabilityQuestionsPage(page),
            ccPolicyInfoPage: new CCPolicyInfoPage(page),
            contactDetailsPage: new ContactDetailsPage(page),
            coverageStatementPage: new CoverageStatementPage(page),
            duplicateCheckPage: new DuplicateCheckPage(page),
            estimatePage: new EstimatePage(page),
            homePage: new HomePage(page),
            insuranceCompanyPage: new InsuranceCompanyPage(page),
            leadgenHomePage: new LeadgenHomePage(page),
            moldingQuestionsPage: new MoldingQuestionsPage(page),
            orderConfirmationPage: new OrderConfirmationPage(page),
            partQuestionsPage: new PartQuestionsPage(page),
            paymentMethodPage: new PaymentMethodPage(page),
            policyInfoSubmittedPage: new PolicyInfoSubmittedPage(page),
            policyVehiclesPage: new PolicyVehiclesPage(page),
            recalibrationInfoPage: new RecalibrationInfoPage(page),
            schedulePage: new SchedulePage(page),
            serviceLocationPage: new ServiceLocationPage(page),
            servicePackagePage: new ServicePackagesPage(page),
            vehicleDamagePage: new VehicleDamagePage(page),
            vehicleLookupAddressPage: new VehicleLookupAddressPage(page),
            vehicleLookupLicensePage: new VehicleLookupLicensePage(page),
            vehiclePartsPage: new VehiclePartQuestionsPage(page),
            vehicleSelectionPage: new VehicleSelectionPage(page),
            verifyDetailsPage: new VerifyDetailsPage(page),
            vinLookupPage: new VinLookupPage(page),
            zipLookupPage: new ZipLookupPage(page),
            endorsementsPage: new EndorsementsPage(page),
            policyDriverPage: new PolicyDriverPage(page)
        };
    }

    protected async dispose(): Promise<void> {
        // Tear down for test case
    }
}