const {SolveCaptcha, TWO_CAPTCHA_CONNECTION_FAILED} = require("./SolveCaptcha");
const ReserveResultPojo = require("../models/ReserveResultPojo");
const appointmentLogger = require("../utiles/LoggerUtils")
const PublishType = require("../models/PublishType");
const {
    shell
} = require('electron')
const GeoCaptchaSolver = require("./GeoCaptchaSolver");
// const RDV_URL = "http://192.168.0.44:8000/test_appointment.html"
const RDV_URL = "https://rendezvousparis.hermes.com/client/register";
BLANK_URL = "about:blank"
const ERROR_CAPTCHA_UNSOLVABLE = "ERROR_CAPTCHA_UNSOLVABLE";

const COUNTRY_ID = "#phone_country"
const PHONE_NUMBER = "#phone_number"
const EMAIL_ID = "#email"
const PREFER_STORE = "#prefer"
const LAST_NAME = "#surname"
const FIRST_NAME = "#name"
const CGU_ID = "#cgu"
const PROCESSING_ID = "#processing"
const PASSPORT_ID = "#passport_id"

const TIME_OUT = 60 * 1000 * 4//4 mins

const CONFIRMED_MESSAGE = "Your request for a Leather Goods appointment has been registered"
const CONFIRMED_MESSAGE_FR = "Votre demande de rendez-vous Maroquinerie a bien été enregistrée et nous vous en remercions."
const MESSAGE_URL_VALIDATION_FR = "Nous avons envoyé un lien par e-mail."
const MESSAGE_URL_VALIDATION_EN = "Please click on the link we sent by email"
const DOUBLE_REQUEST_ERROR_MESSAGE = "A request with the same data has already been validated today."
const DOUBLE_REQUEST_ERROR_MESSAGE_FR = "Une demande avec les données saisies a déjà été validée aujourd’hui."
const TOO_MANY_REQUEST_ERROR_MESSAGE = "Due to a large number of requests"
const TOO_MANY_REQUEST_ERROR_MESSAGE_FR = "Suite à un trop grand nombre de demandes"
const CAPTCHA_ERROR_MESSAGE = "Error verifying captcha, please try again"
const CAPTCHA_ERROR_MESSAGE_FR = "La vérification du captcha a échoué"
REGEX_RDV_URL = "https:\/\/rendezvousparis\.hermes\.com\/client\/register\/[A-Z0-9]+"
const DEFAULT_STORE = 'faubourg';

function delay(delayInMs) {
    return new Promise(resolve => {
        setTimeout(() => {
            resolve(2);
        }, delayInMs);
    });
}

function getRandom() {
    return Math.floor(Math.random() * 3);
}

function getRandomWaitTime() {
    return getRandom() * 1000
}

function log(message) {
    appointmentLogger.log({level: "info", message: message})
}

class CommandorPage {
    constructor(contact, device, mongoManager, selectedStore = DEFAULT_STORE, audioAnalyse = false) {
        this.contact = contact;
        this.device = device;
        this.mongoManager = mongoManager;
        this.selectedStore = selectedStore;
        this.choosedStore = selectedStore
        this.isFillingFields = false;
        this.isTerminated = false;
        this.audioAnalyse = audioAnalyse;
    }


    async loadPage() {
        // Connect to the device.
        log("loadPage() called");
        // await this.device.shell('am force-stop com.android.chrome');
        const context = await this.device.launchBrowser();
        // await context.clearCookies()

        // Use BrowserContext as usual.
        this.page = await context.newPage();
        this.page.on("load", (loadedPage) => {
            this.onPageLoad(loadedPage)
        })

        this.page.on("response", (response) => {
            this.onResponse(response)
        })
        try {
            await this.page.goto(RDV_URL, {timeout: 90 * 1000});
        } catch (e) {
            log(e)
            this.isTerminated = true
        }

        let cancel
        const intervalTask = setInterval(() => {
            if (this.isTerminated) {
                log("request terminated, will close device")
                context.close()
                // this.page.close()
                this.device.close()
                clearInterval(intervalTask)
                cancel()
                return context
            }
        }, 10 * 1000)//interval of 10 seconds

        await new Promise(function (fulfill, reject) {
            cancel = function () {
                fulfill(Promise.resolve())
            }
            setTimeout(fulfill, TIME_OUT, 5)
        }).then(log)
    }

    async chooseCountry(page) {
        if (!page.isClosed()) {
            try {
                await page.locator(COUNTRY_ID).focus();
                await delay(getRandomWaitTime())
                await page.click(COUNTRY_ID);
                await delay(getRandomWaitTime())
                await page.selectOption(COUNTRY_ID, 'FR');
                await delay(getRandomWaitTime())
            } catch (e) {
                log(e);
                this.isTerminated = true;
            }
        }
    }

    async fillEmail(page) {
        try {
            if (!page.isClosed()) {
                await page.locator(EMAIL_ID).focus();
                await delay(getRandomWaitTime())
                await page.locator(EMAIL_ID).fill(this.contact.mail);
            }
        } catch (e) {
            log(e)
        }
    }

    async inputPhoneNumber(page) {
        try {
            if (!page.isClosed()) {
                await page.locator(PHONE_NUMBER).focus()
                await page.locator(PHONE_NUMBER).fill("0" + this.contact.phoneNumber)
            }
        } catch (e) {
            log(e);
            this.isTerminated = true;
        }

    }

    async inputName(page) {
        try {
            if (!page.isClosed()) {
                await page.locator(LAST_NAME).focus()
                await delay(getRandomWaitTime())
                await page.locator(LAST_NAME).fill(this.contact.lastName)
                await page.locator(FIRST_NAME).focus()
                await delay(getRandomWaitTime())
                await page.locator(FIRST_NAME).fill(this.contact.firstName)
            }
        } catch (e) {
            log(e);
            this.isTerminated = true
        }
    }

    async inputPassportId(page) {
        try {
            if (!page.isClosed()) {

                await page.locator(PASSPORT_ID).focus()
                await delay(getRandomWaitTime())
                await page.locator(PASSPORT_ID).fill(this.contact.passportNumber.toString())
            }
        } catch (e) {
            log(e);
            this.isTerminated = true;
        }

    }

    async checkCGU(page) {
        try {
            if (!page.isClosed()) {
                await page.locator(CGU_ID).focus()
                await page.locator(CGU_ID).click()
                await delay(getRandomWaitTime())
                await page.locator(PROCESSING_ID).focus()
                await page.locator(PROCESSING_ID).click()
            }
        } catch (e) {
            log(e);
            this.isTerminated = true;
        }
    }

    async chooseStore(page) {
        try {
            if (!page.isClosed()) {
                if (this.selectedStore !== "random") {
                    await page.locator(PREFER_STORE).focus()
                    await delay(1000)
                    await page.click(PREFER_STORE);
                    let stores = this.selectedStore.split(":")
                    this.choosedStore = stores[Math.floor(Math.random() * stores.length)]
                    await page.selectOption(PREFER_STORE, this.choosedStore);
                }
            }
        } catch (e) {
            log(e);
            this.isTerminated = true;
        }
        // await page.click(PREFER_STORE);
    }


    async fillFields(page) {
        log("fillFields called")
        if (!this.isFillingFields) {
            this.isFillingFields = true;
            await this.chooseStore(page);
            await this.inputName(page);
            await this.chooseCountry(page);
            await this.inputPhoneNumber(page)
            await this.fillEmail(page)
            await this.inputPhoneNumber(page)
            await this.inputPassportId(page)
            await this.checkCGU(page)
            await this.resolveCaptcha(page)
            await delay(10 * 1000)
        }
    }

    async clickValid(page) {
        await delay(getRandomWaitTime())
        try {
            if (!this.page.isClosed()) {
                this.page.evaluate(() => {
                    let element = document.getElementsByClassName("btn")[0];
                    if (typeof element !== 'undefined')
                        document.getElementsByClassName("btn")[0].focus();
                })
                await delay(getRandomWaitTime())
                if (!this.page.isClosed()) {
                    try {
                        this.page.evaluate(() => {
                            document.getElementsByClassName("btn")[0].click();
                        })
                    } catch (e) {
                        log(e)
                    }
                }
            }
        } catch (e) {
            log(e)
        }
    }

    async resolveCaptcha(page) {
        if (RDV_URL.includes("192")) {
            await this.push_message_to_queue(PublishType.SUCCESS)
            return
        }
        this.captchaSolver = new SolveCaptcha(page);
        await this.captchaSolver.start((solution) => {
            log("solution is: " + solution);
            if (solution !== ERROR_CAPTCHA_UNSOLVABLE && solution !== TWO_CAPTCHA_CONNECTION_FAILED) {
                try {
                    if (!page.isClosed()) {
                        page.evaluate((solution) => {
                            let element = document.getElementById("g-recaptcha-response");
                            if (element != null)
                                document.getElementById("g-recaptcha-response").innerHTML = solution;
                        }, solution)
                        this.clickValid();
                    }
                } catch (e) {
                    log(e)
                    this.isTerminated = true;
                }
            } else {
                this.isTerminated = true;
            }
        })
    }

    async onPageLoad(currentPage) {
        try {
            let content = await currentPage.content();
            let captcha_url = "geo.captcha-delivery.com/captcha";
            if (content.toString().includes(captcha_url)) {
                if (this.audioAnalyse) {
                    await this.checkAudioBtn();
                }
                for (let i = 0; i < 15; i++) {
                    await delay(1000)
                    shell.beep()
                }
                log("发现datadome");
            } else {
                if (currentPage.url() === RDV_URL) {
                    await this.fillFields(this.page);
                    if (this.isFillingFields)
                        await this.getErrors()
                } else {
                    if (content.includes(MESSAGE_URL_VALIDATION_FR) || content.includes(MESSAGE_URL_VALIDATION_EN)) {
                        log("successful");
                        await this.push_message_to_queue(PublishType.SUCCESS);
                    } else {
                        //    try to get errors
                        await this.getErrors()
                    }
                }
            }
        } catch (e) {
            log(e)
        }
    }

    async checkAudioBtn() {
        let audioBtn = await this.page.frameLocator("iframe").locator("#captcha__audio__button");
        if (audioBtn) {
            log("audioBtn found")
            audioBtn.click()
            let captchaSolver = new GeoCaptchaSolver(this.page)
            await captchaSolver.solve()
        } else {
            log("audioBtn not found")
        }
    }

    async onResponse(response) {
        // let rex = new RegExp(REGEX_RDV_URL)
        // log("onResponse with url:" + response.url())
        // if (rex.test(response.url())) {
        //     log("rdv url found:" + response.url())
        //     await this.push_message_to_db(PublishType.SUCCESS, response.url())
        // }
    }

    async push_message_to_queue(publishType) {
        let url = this.page.url();
        let splitedUrl = url.split("/");
        let id = splitedUrl[splitedUrl.length - 1];
        if (url === "https://rendezvousparis.hermes.com/client/welcome") {
            return
        }
        //    save to mongoDb
        let reserve = ReserveResultPojo.create_from_contact(this.contact, id, this.page.url(), this.choosedStore, publishType);
        reserve.source_from = this.device.model();
        await this.mongoManager.saveReserveToDb(reserve.to_mongo_dict())
        this.isTerminated = true
    }

    async push_message_to_db(publishType, url) {
        let splitedUrl = url.split("/");
        let id = splitedUrl[splitedUrl.length - 1];
        if (url === "https://rendezvousparis.hermes.com/client/welcome") {
            return
        }
        //    save to mongoDb
        let reserve = ReserveResultPojo.create_from_contact(this.contact, id, this.page.url(), 1, publishType);
        reserve.source_from = this.device.model();
        await this.mongoManager.saveReserveToDb(reserve.to_mongo_dict())
        await delay(2 * 1000)
        this.isTerminated = true
    }


    async getErrors() {
        if (this.page.url() === BLANK_URL) {
            this.isTerminated = true;
        } else {
            try {
                let errorItem = this.page.locator("div.alert");
                if (errorItem) {
                    let errorContent = await errorItem.innerHTML();
                    log("error:" + errorContent);
                    this.handleError(errorContent);
                }
            } catch (e) {
                log(e);
            }
        }
    }

    handleError(errorContent) {
        if (errorContent.includes(DOUBLE_REQUEST_ERROR_MESSAGE) || errorContent.includes(DOUBLE_REQUEST_ERROR_MESSAGE_FR)) {
            this.isTerminated = true;
        } else if (errorContent.includes(TOO_MANY_REQUEST_ERROR_MESSAGE) || errorContent.includes(TOO_MANY_REQUEST_ERROR_MESSAGE_FR)) {
            //    todo, add contact to black list
            this.isTerminated = true;
        } else if (errorContent.includes(CAPTCHA_ERROR_MESSAGE) || errorContent.includes(CAPTCHA_ERROR_MESSAGE_FR)) {
            this.isTerminated = true;
        }

    }
}

module.exports = CommandorPage