const {SolveCaptcha, TWO_CAPTCHA_CONNECTION_FAILED} = require("./SolveCaptcha");
const ReserveResultPojo = require("../models/ReserveResultPojo");
const appointmentLogger = require("../utiles/LoggerUtils")
const OCRResult = require("../models/OCRResult");
const PublishType = require("../models/PublishType");
const puppeteer = require('puppeteer');
const GeoCaptchaSolver = require("./GeoCaptchaSolver");
const SlidingCaptchaSolver = require("./SlidingCaptchaSolver");
const OCRChecker = require("./OCRChecker");
const {exec} = require("child_process");
const {openUrlWithAdb} = require("../utiles/CmdUtils");
// const RDV_URL = "http://192.168.0.44:8000/test_appointment.html"
const RDV_URL = "https://rendezvousparis.hermes.com/client/register";
const 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 CAPTCHA_CONTAINER = "#captcha-container";

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

const EMPTY_RESPONSE_ERROR = "ERR_EMPTY_RESPONSE"
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 REGEX_RDV_URL = "https:\/\/rendezvousparis\.hermes\.com\/client\/register\/[A-Z0-9]+"
const DEFAULT_STORE = 'faubourg';
const timeout = (prom, time) =>
    Promise.race([prom, new Promise((_r, rej) => setTimeout(rej, time))]);

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})
}

async function clearApp(device, packageName) {
    let cmd = `adb -s ${device.serial()} shell pm clear ${packageName}`
    logWithDevice("cmd is " + cmd, device)
    await exec(cmd);
}

async function exceutShellCmd(device, cmdToExecut) {
    let cmd = `adb -s ${device.serial()} shell ${cmdToExecut}`
    logWithDevice("cmd is " + cmd, device)
    await exec(cmd);
}

function logWithDevice(message, device) {
    appointmentLogger.log({level: "info", message: device.model() + ":" + device.serial() + ":" + message})
}

class CommandorPage {
    constructor(contact, device, sender, mongoManager, selectedStore = DEFAULT_STORE, audioAnalyse = false, alertBeep = false, port = 9000) {
        this.contact = contact;
        this.device = device;
        this.mongoManager = mongoManager;
        this.selectedStore = selectedStore;
        this.choosedStore = selectedStore;
        this.port = port;
        this.sender = sender;
        this.ocrChecker = new OCRChecker(this.device, this.contact);
        this.browserPackageName = "com.brave.browser";
        // this.browserPackageName = "com.android.chrome";
        this.isFillingFields = false;
        this.isTerminated = false;
        this.cguChecked = false;
        this.isNameInput = false;
        this.isEmailFilled = false;
        this.isCountryChoosen = false;
        this.isPhoneInput = false;
        this.firstStart = true;
    }

    async connect_to_browser(ocrResult) {
        console.log("connect_to_browser() called");
        if (this.browser === undefined) {
            this.isTerminated = true
            return
        }
        console.log("browser.isConnected: " + this.browser.isConnected());
        if (!this.browser.isConnected()) {
            this.browser = await puppeteer.connect({
                browserWSEndpoint: "ws://127.0.0.1:" + this.port + "/devtools/browser",
                headless: false, defaultViewport: null
            })
        }
        let pages = await this.browser.pages();
        // 0 is the last page
        pages.forEach((currentPage) => {
            let url = currentPage.url()
            if (url.includes(RDV_URL)) {
                this.page = currentPage
            }
        })
        logWithDevice("pageUrl is " + this.page.url(), this.device)
        // this.page = pages[pages.length - 1];
        switch (ocrResult) {
            case OCRResult.SUCCESS:
                //         get url and push to server
                await this.push_message_to_queue(PublishType.SUCCESS)
                break;
            case OCRResult.RECAPTCHA_ERROR:
                //         get url and push to server
                await this.fillFields()
                break;
            case OCRResult.SLIDING_CAPTCHA_REFRESH:
                pages.forEach((currentPage) => {
                    currentPage.close()
                })
                break;
            case OCRResult.TO_REFRESH:
                logWithDevice("will reload page", this.device)
                let cmd = "input swipe 382 682 382 1682"
                logWithDevice("will send cmd:" + cmd, this.device)
                this.device.shell(cmd);
                await delay(3000);
                await this.clickOnConfirmBtn();
                await this.checkResultWithOcr();
                break;
        }
    }

    async checkIfSuccessful() {
        let content = await this.page.content();
        if (content.includes(MESSAGE_URL_VALIDATION_FR) || content.includes(MESSAGE_URL_VALIDATION_EN)) {
            log("successful");
            await this.push_message_to_queue(PublishType.SUCCESS);
            return true;
        } else {
            return false
        }
    }

    async loadPage() {
        logWithDevice(this.device.serial() + ":loadPage() called, with port:" + this.port, this.device);
        try {
            // let cmd = 'adb -s ' + device.serial() + " forward tcp:" + attributedPort + " localabstract:chrome_devtools_remote";
            await exceutShellCmd(this.device, " forward tcp:" + this.port + " localabstract:chrome_devtools_remote")
            await delay(1 * 1000);
            // await this.startPage(this.device, this.browserPackageName + "/com.google.android.apps.chrome.Main")
            await delay(4 * 1000);
        } catch (e) {
            console.log(e)
            this.isTerminated = true;
        }
        // await this.acceptCookies();
        await this.enableDisableAirPlanMode();
        // await delay(10 * 1000);
        await openUrlWithAdb(RDV_URL, this.device)
        await delay(10 * 1000)
        await this.checkResultWithOcr()

        let cancel
        const intervalTask = setInterval(async () => {
            if (this.isTerminated) {
                log(this.device.model() + ":request terminated, send cancel()");
                try {
                    if (this.page !== undefined) {
                        await this.page.close()
                    }
                } catch (e) {
                    console.log(e)
                }
                this.disconnectBrowser();
                clearInterval(intervalTask)
                cancel()
            } else {
            }
        }, 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 clickOnlineAppointment() {
        try {
            logWithDevice("will click on rdv link", this.device);
            await this.connectBrowserIfNecessary();
            const [button] = await this.page.$x("//a[contains(., 'rendezvousparis')]");
            console.log("button is " + button)
            if (button) {
                console.log("will click on the button")
                try {
                    await button.click();
                    if (this.firstStart) {
                        this.disconnectBrowser()
                    }
                    await delay(2000)
                    await this.checkResultWithOcr();
                } catch (e) {
                    console.log(e)
                }
            }
        } catch (e) {
            console.log(e)
            await this.checkResultWithOcr();
        }
    }

    async acceptCookies() {
        logWithDevice("will accept Cookies", this.device);
        if (this.browser.isConnected() && !this.page.isClosed()) {
            try {
                await this.page.evaluate(() => {
                    let buttons = document.getElementsByTagName('button');
                    for (let i = 0; i < buttons.length; i++) {
                        let button = buttons[i];
                        let txt = button.textContent || button.innerText;
                        if (txt.includes("Tout accepter")) {
                            button.click()
                        }
                        console.log(txt);
                    }
                })
            } catch (e) {
                console.log(e)
            }

        }
    }

    async chooseCountry(page) {
        logWithDevice("chooseCountry", this.device)
        if (this.browser.isConnected() && !this.isTerminated) {
            try {
                if (!this.isCountryChoosen) {
                    // await page.focus(COUNTRY_ID);
                    // await delay(getRandomWaitTime())
                    // await page.click(COUNTRY_ID);
                    // await delay(getRandomWaitTime())
                    await page.select(COUNTRY_ID, 'FR');
                    await delay(getRandomWaitTime())
                    this.isCountryChoosen = true;
                }
            } catch (e) {
                log(e);
                this.isTerminated = true;
            }
        }
    }

    async fillEmail(page) {
        logWithDevice("fillEmail", this.device)
        try {
            if (this.browser.isConnected() && !this.isTerminated) {
                if (!this.isEmailFilled) {
                    await page.focus(EMAIL_ID);
                    await delay(getRandomWaitTime())
                    console.log("will clear email field");
                    await page.evaluate((email) => {
                        let field = document.getElementById("email");
                        if (field) {
                            field.value = email
                        }
                    }, this.contact.mail)
                    // await page.evaluate(() => {
                    //     let field = document.getElementById("email");
                    //     if (field) {
                    //         field.value = ""
                    //     }
                    // })
                    // await delay(1000);
                    // await page.keyboard.type(this.contact.mail);
                    this.isEmailFilled = true;
                }
            }
        } catch (e) {
            log(e)
        }
    }

    async inputPhoneNumber(page) {
        logWithDevice("inputPhoneNumber", this.device)
        try {
            if (this.browser.isConnected() && !this.isTerminated) {
                if (!this.isPhoneInput) {
                    await page.focus(PHONE_NUMBER);
                    console.log("will clear phone_number field");
                    await page.evaluate((phone) => {
                        let field = document.getElementById("phone_number");
                        if (field) {
                            field.value = phone
                        }
                    }, "" + this.contact.phoneNumber)
                    // }, "0" + this.contact.phoneNumber)
                    // 如果需要显示键盘的话，使用下面方法
                    // await page.evaluate(() => {
                    //     let field = document.getElementById("phone_number");
                    //     if (field) {
                    //         field.value = ""
                    //     }
                    // })
                    // await delay(1000);
                    // await page.keyboard.type("0" + this.contact.phoneNumber);
                    this.isPhoneInput = true;
                }
            }
        } catch (e) {
            log(e);
            this.isTerminated = true;
        }

    }

    async inputName(page) {
        logWithDevice("inputName", this.device)
        await delay(getRandomWaitTime());
        try {
            logWithDevice("input name called with this.browser.isConnected=" + this.browser.isConnected(), this.device)
            logWithDevice("input name called with this.isNameInput=" + this.isNameInput, this.device)
            if (this.browser.isConnected() && !this.isTerminated && !this.page.isClosed()) {
                if (!this.isNameInput) {
                    await page.focus(LAST_NAME);
                    await delay(getRandomWaitTime());
                    console.log("will clear surname field");
                    await page.evaluate(() => {
                        let field = document.getElementById("surname");
                        if (field) {
                            field.value = ""
                        }
                    })
                    await delay(1000);
                    // await page.keyboard.type(this.contact.lastName);
                    await page.evaluate((lastName) => {
                        let field = document.getElementById("surname");
                        if (field) {
                            field.value = lastName
                        }
                    }, this.contact.lastName)
                    await page.focus(FIRST_NAME);
                    await delay(getRandomWaitTime());
                    // console.log("will clear name field");
                    await page.evaluate((firstName) => {
                        let field = document.getElementById("name");
                        if (field) {
                            field.value = firstName;
                            //     field.value = ""
                        }
                    }, this.contact.firstName)
                    // await page.keyboard.type(this.contact.firstName);
                    this.isNameInput = true;
                }
            }
        } catch (e) {
            logWithDevice("exception while inputting name", this.device)
            console.log(e);
            this.isTerminated = true
        }
    }

    async getRawAppointmentInfo(page) {
        let passportId = await page.evaluate(() => {
            let field = document.getElementById("passport_id");
            return field.value
        })

        let lastName = await page.evaluate(() => {
            let field = document.getElementById("surname");
            return field.value
        })
        let firstName = await page.evaluate(() => {
            let field = document.getElementById("name");
            return field.value
        })
        let phone = await page.evaluate(() => {
            let field = document.getElementById("phone_number");
            return field.value
        })

        let email = await page.evaluate(() => {
            let field = document.getElementById("email");
            return field.value
        })
        return new ReserveResultPojo(email, phone, passportId, email, lastName, firstName, "random", "", PublishType.PENDING)
    }

    async inputPassportId(page) {
        logWithDevice("inputPassportId", this.device)
        try {
            if (this.browser.isConnected() && !this.isTerminated) {
                if (!this.isPasspordInput) {
                    await page.focus(PASSPORT_ID);
                    await delay(getRandomWaitTime());
                    console.log("will clear passport_id field");
                    await page.evaluate((passport) => {
                        let field = document.getElementById("passport_id");
                        if (field) {
                            field.value = passport
                        }
                    }, this.contact.passportNumber.toString())
                    // await page.evaluate(() => {
                    //     let field = document.getElementById("passport_id");
                    //     if (field) {
                    //         field.value = ""
                    //     }
                    // })
                    // await delay(1000);
                    // await page.keyboard.type(this.contact.passportNumber.toString());
                    this.isPasspordInput = true;
                }
            }
        } catch (e) {
            log(e);
            this.isTerminated = true;
        }

    }

    async checkCGU(page) {
        logWithDevice("checkCGU", this.device)
        try {
            if (this.browser.isConnected() && !this.isTerminated && !this.page.isClosed()) {
                if (!this.cguChecked) {
                    await page.focus(CGU_ID);
                    await page.evaluate(() => {
                        if (!document.querySelector("#cgu").checked) {
                            document.querySelector("#cgu").checked = true
                        }
                    });
                    // await page.click(CGU_ID);
                    await delay(getRandomWaitTime());
                    await page.focus(PROCESSING_ID);
                    // await page.click(PROCESSING_ID)
                    await page.evaluate(() => {
                        if (!document.querySelector("#processing").checked) {
                            document.querySelector("#processing").checked = true
                        }
                    });
                    this.cguChecked = true;
                }
            }
        } catch (e) {
            log(e);
            this.isTerminated = true;
        }
    }

    async chooseStore(page) {
        logWithDevice("chooseStore", this.device)
        await delay(getRandomWaitTime())
        try {
            if (!this.page.isClosed() && !this.isTerminated) {
                if (this.contact.store === undefined || this.contact.store.length === 0) {
                    this.contact.store = this.selectedStore;
                } else {
                    this.selectedStore = this.contact.store;
                }
                if (this.contact.store !== "random") {
                    // page.focus(PREFER_STORE);
                    await delay(500)
                    // page.click(PREFER_STORE);
                    let stores = this.contact.store.split(":")
                    this.choosedStore = stores[Math.floor(Math.random() * stores.length)]
                    await page.select(PREFER_STORE, this.choosedStore);
                }
            }
        } catch (e) {
            log(e);
            // this.isTerminated = true;
        }
    }


    async fillFields(page) {
        logWithDevice("fillFields called for contact: " + this.contact.mail, this.device)
        logWithDevice("this.isFillingFields: " + this.isFillingFields, this.device);
        logWithDevice("this.isTerminated: " + this.isTerminated, this.device);
        if (!this.isFillingFields && !this.isTerminated && !this.page.isClosed()) {
            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.inputPassportId(page)
            await this.checkCGU(page)
            let pageContent = await page.content()
            let hasCaptcha = pageContent.includes("g-recaptcha-response")
            if (hasCaptcha) {
                await this.resolveCaptcha(page);
                await delay(getRandomWaitTime());
                this.isFillingFields = false;
            } else {
                await delay(getRandomWaitTime())
                try {
                    await this.clickValid();
                } catch (e) {
                    this.isTerminated = true;
                    console.log(e)
                }
                this.isFillingFields = false
            }
        }
    }

    async clickValid() {
        logWithDevice("clickValid() called.", this.device);
        await delay(getRandomWaitTime())
        this.isFillingFields = false
        try {
            if (!this.page.isClosed()) {
                if (!this.page.isClosed()) {
                    try {
                        // remove debug flag
                        // const validElement = await page.$('.btn');
                        logWithDevice("will click on valid button", this.device);
                        logWithDevice("isTerminated is " + this.isTerminated, this.device);
                        if (!this.isTerminated) {
                            if (!this.page.isClosed()) {
                                let raw = await this.getRawAppointmentInfo(this.page)
                                if (raw.email !== undefined) {
                                    this.contact.mail = raw.email
                                }
                                if (raw.passportNumber !== undefined) {
                                    this.contact.passportNumber = raw.passportNumber
                                }
                                if (raw.lastName !== undefined) {
                                    this.contact.lastName = raw.lastName
                                }
                                if (raw.phoneNumber !== undefined) {
                                    this.contact.phoneNumber = raw.phoneNumber
                                }
                                if (raw.firstName !== undefined) {
                                    this.contact.firstName = raw.firstName
                                }
                                await this.page.evaluate(() => {
                                    document.getElementsByClassName("btn")[0].click();
                                })
                                if (this.firstStart) {
                                    this.disconnectBrowser();
                                    await delay(3000);
                                    await this.checkResultWithOcr();
                                }
                            }
                        }
                    } catch (e) {
                        log(e);
                        await this.checkResultWithOcr()
                    }
                }
            }
        } catch (e) {
            logWithDevice("exception received", this.device)
            log(e)
        }
    }

    async resolveCaptcha(page) {
        logWithDevice("resolveCaptcha", this.device)
        if (RDV_URL.includes("192")) {
            // await this.push_message_to_queue(PublishType.SUCCESS)
            await delay(100000)
            return
        }
        try {
            //check whether there is captcha
            let pageContent = await page.content()
            let hasCaptcha = pageContent.includes("g-recaptcha-response")
            logWithDevice("hasCaptcha = " + hasCaptcha, this.device)
            if (hasCaptcha) {
                // await this.clickCheckbox()
                await delay(1000)
                this.captchaSolver = new SolveCaptcha(page);
                await this.captchaSolver.start((solution) => {
                    logWithDevice("solution is: " + solution, this.device);
                    if (solution !== ERROR_CAPTCHA_UNSOLVABLE && solution !== TWO_CAPTCHA_CONNECTION_FAILED) {
                        try {
                            if (!page.isClosed() && !this.isTerminated) {
                                if (this.browser.isConnected()) {
                                    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;
                    }
                })
            } else {
                await this.clickValid();
            }
        } catch (e) {
            console.log(e)
            this.isTerminated = true;
        }
    }

    async onPageLoad(currentPage) {
        logWithDevice("onPageLoad called with url " + this.page.url(), this.device)
        try {
            let content = await this.page.content();
            let captcha_url = "geo.captcha-delivery.com/captcha";
            // if (content.toString().includes(captcha_url)) {
            //     await this.checkResultWithOcr()
            //     logWithDevice("发现datadome", this.device);
            // } else
            if (content.includes("502 Bad Gateway")) {
                logWithDevice("502 Bad Gateway found", this.device)
                await this.page.reload()
            } else if (this.page.url().includes("sorry")) {
                await this.resetBrowser()
            } else if (content.includes("PROXY_CONNECTION_FAILED")) {
                logWithDevice("PROXY_CONNECTION_FAILED, will reload page", this.device);
                await delay(2000)
                await this.page.reload()
            } else if (content.includes("ERR_NETWORK_CHANGED")) {
                logWithDevice("ERR_NETWORK_CHANGED, will reload page", this.device);
                await delay(2000)
                await this.page.reload()
            } else if (content.includes("ERR_TIMED_OUT")) {
                logWithDevice("ERR_TIMED_OUT, will reload page", this.device);
                await delay(2000)
                await this.page.reload()
            } else if (content.includes("408 Request Time-out")) {
                logWithDevice("Request Time-out, will reload page", this.device);
                await delay(2000)
                await this.page.reload()
            } else {
                if (this.page.url() === RDV_URL) {
                    await this.fillFields(this.page);
                } 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 if (content.includes(EMPTY_RESPONSE_ERROR)) {
                        log("EMPTY_RESPONSE_ERROR error received, will quit")
                        this.isTerminated = true
                    } else {
                        //    try to get errors
                        await this.getErrors()
                    }
                }
            }
        } catch (e) {
            log(e)
        }
    }


    async slidingCaptcha(onResult) {
        logWithDevice("slidingCaptcha", this.device);
        if (this.device.model() === "MI 5s" || this.device.model() === "ASUS_X00QD" || this.device.model() === "ASUS_Z012D" || this.device.model() === "HUAWEI NXT-TL00") {
            let cmd = `adb -s ${this.device.serial()} shell input touchscreen swipe 900 495 900 195`
            await exec(cmd);
            await delay(5000);
        }
        let slidingCaptchaSolver = new SlidingCaptchaSolver(this.device);
        await slidingCaptchaSolver.solve(this.page, async (isSuccessful) => {
            console.log("check isAlwaysBlocked")
            this.isFillingFields = false
            onResult(isSuccessful)
        })
    }

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

    async push_message_to_queue(publishType) {
        let url = this.page.url();
        logWithDevice("successful url is " + url, this.device)
        await this.push_message_to_db(publishType, url)
        // this.firstStart = false;
    }

    async push_message_to_db(publishType, url) {
        let splitedUrl = url.split("/");
        let id = splitedUrl[splitedUrl.length - 1];
        if (id === "register") {
            id = this.contact.mail;
        }
        if (url === "https://rendezvousparis.hermes.com/client/welcome") {
            return
        }
        //    save to mongoDb
        let reserve = ReserveResultPojo.create_from_contact(this.contact, id, url, this.choosedStore, publishType);
        reserve.source_from = this.device.model();
        await this.mongoManager.saveReserveToDb(reserve.to_mongo_dict())
        if (!this.page.isClosed()) {
            try {
                await this.page.close();
            } catch (e) {
                console.log(e)
            }
        }
        // await this.deleteFromBlackList()
        this.isTerminated = true
    }

    async getErrors() {
        logWithDevice("getErrors", this.device);
        await this.checkResultWithOcr();
        if (this.page.url() === BLANK_URL) {
            this.isTerminated = true;
        }
    }

    async clickCheckbox() {
        logWithDevice("clickCheckbox", this.device);

        try {
            // let errorItem = await this.page.click("#recaptcha-anchor > div.recaptcha-checkbox-border")
            await this.page.waitForSelector("iframe")
            let elementHandle = await this.page.$('[title="reCAPTCHA"]')
            const iframe = await elementHandle.contentFrame()
            await iframe.click("#recaptcha-anchor > div.recaptcha-checkbox-border")
        } catch (e) {
            log(e);
        }
    }

    async resetBrowser() {
        logWithDevice("resetBrowser", this.device);
        this.isFillingFields = false;
        this.cguChecked = false;
        this.isNameInputing = false;
        this.isEmailFilled = false;
        this.isCountryChoosen = false;
        this.isPhoneInput = false;
        this.isPasspordInput = false;
        await clearApp(this.device, this.browserPackageName)
        // await this.device.shell("pm clear " + this.browserPackageName)
        await delay(1000)
        this.isTerminated = true;
        // await this.checkResultWithOcr();
    }


    disconnectBrowser() {
        try {
            logWithDevice("will disconnect browser", this.device);
            if (this.browser !== undefined) {
                this.browser.disconnect();
            }
        } catch (e) {
            console.log(e)
        }
    }

    async connectBrowserIfNecessary() {
        if (!this.browser.isConnected()) {
            try {
                this.browser = await puppeteer.connect({
                    browserWSEndpoint: "ws://127.0.0.1:" + this.port + "/devtools/browser",
                    headless: false, defaultViewport: null
                })
            } catch (e) {
                console.log(e)
                this.isTerminated = true;
            }
        }
    }

    async checkResultWithOcr() {
        logWithDevice("checkResultWithOcr() called.", this.device)
        if (this.device.model() === "M2006C3LG")
            await delay(6000);
        else {
            await delay(4000);
        }
        let checkResult = await this.ocrChecker.get_result();
        console.log(checkResult);
        while (checkResult === OCRResult.RECHECK || checkResult === OCRResult.NO_INTERNET) {
            logWithDevice("will recheck OCR", this.device)
            await delay(4000)
            logWithDevice("will recheck OCR", this.device)
            checkResult = await this.ocrChecker.get_result();
        }
        while (checkResult === OCRResult.SLIDING_CAPTCHA) {
            logWithDevice("will call this.slidingCaptcha()", this.device)
            await this.slidingCaptcha(async (isSuccessful) => {
                logWithDevice("SLIDING_CAPTCHA result is " + isSuccessful, this.device)
                console.log(checkResult);
                if (isSuccessful) {
                    await delay(5 * 1000)
                    // checkResult = await this.ocrChecker.get_result();
                    await this.checkResultWithOcr()
                } else {
                    if (checkResult === OCRResult.SLIDING_CAPTCHA) {
                        checkResult = OCRResult.TERMINAED
                        this.isTerminated = true
                    }
                }
            })
            await delay(10 * 1000)
        }
        switch (checkResult) {
            case OCRResult.SLIDING_CAPTCHA_LOADING:
                this.isTerminated = true;
                break;
            case OCRResult.SSL_CERT_ERROR:
                await this.resetBrowser()
                break;
            case OCRResult.TERMINAED:
                this.isTerminated = true;
                break;
            case OCRResult.SLIDING_CAPTCHA_REFRESH:
                await this.connect_to_browser(checkResult)
                break;
            case OCRResult.FILL_FIELD:
                logWithDevice("FILL_FIELD", this.device)
                if (this.browser === undefined || !this.browser.isConnected()) {
                    logWithDevice("trying to connect to browser", this.device)
                    try {
                        this.browser = await puppeteer.connect({
                            browserWSEndpoint: "ws://127.0.0.1:" + this.port + "/devtools/browser",
                            headless: false, defaultViewport: null
                        })

                        try {
                            logWithDevice("get pages", this.device)
                            // add listeners
                            let pages = await timeout(this.browser.pages(), 5 * 1000);
                            pages.forEach((currentPage) => {
                                if (currentPage.url() === RDV_URL) {
                                    this.page = currentPage;
                                } else {
                                    try {
                                        if (!this.isTerminated)
                                            currentPage.close()
                                    } catch (e) {
                                        console.log(e)
                                    }
                                }
                            })
                            logWithDevice("this.page.bringToFront();", this.device)
                            const cookiesSet = await this.page.cookies();
                            let cookiesString = ""
                            cookiesSet.forEach((cookie) => {
                                cookiesString = cookiesString + cookie.name + "=" + cookie.value + ";"
                            })
                            console.log(cookiesString);
                            this.sender.sendMessage(cookiesString)
                            // let content = await this.page.content()
                            // console.log(content)
                            await this.page.bringToFront();
                            await this.fillFields(this.page)
                            await delay(2 * 1000);
                        } catch (e) {
                            console.log(e)
                            await this.resetBrowser()
                            // this.isTerminated = true;
                        }
                        // try {
                        //     for (const currentPage of pages) {
                        //         if (currentPage.url() === RDV_URL) {
                        //             logWithDevice("get content", this.device)
                        //             let pageContent = await currentPage.content()
                        //             logWithDevice("content:" + pageContent, this.device)
                        //             if (!pageContent.includes("geo.captcha-delivery.com")) {
                        //                 this.page = currentPage;
                        //                 break
                        //             } else {
                        //                 try {
                        //                     await currentPage.close()
                        //                 } catch (e) {
                        //                     console.log(e)
                        //                 }
                        //             }
                        //         } else {
                        //             try {
                        //                 await currentPage.close()
                        //             } catch (e) {
                        //                 console.log(e)
                        //             }
                        //         }
                        //     }
                        // } catch (e) {
                        //     this.isTerminated = true;
                        //     console.log(e)
                        // }

                    } catch (e) {
                        console.log(e)
                        this.isTerminated = true
                    }
                } else {
                    logWithDevice("this.browser.isConnected() is " + this.browser.isConnected(), this.device)
                    try {
                        if (this.browser.isConnected()) {
                            logWithDevice("will use old page", this.device)
                            logWithDevice("this.page.bringToFront();", this.device)
                            await this.page.bringToFront();
                            // this.page = pages;
                            // this.page.await
                            await this.fillFields(this.page)
                            await delay(2 * 1000);
                        }
                    } catch
                        (e) {
                        console.log(e)
                        this.isTerminated = true
                    }
                }

                break;
            case
            OCRResult.SUCCESS:
                //         reconnect to page and get url
                if (!this.isTerminated) {
                    await this.connect_to_browser(OCRResult.SUCCESS)
                    // if (!this.isTerminated) {
                    //     logWithDevice("will save success appointment", this.device)
                    //     await this.push_message_to_queue(PublishType.SUCCESS);
                    //     await this.closePage()
                    // }
                }
                break;
            case
            OCRResult.RECAPTCHA_ERROR:
                this.isTerminated = true;
                break;
            case
            OCRResult.BRAVE_SKIP:
                await this.handleBraveSkipBtn();
                await this.checkResultWithOcr();
                break;
            case OCRResult.BRAVE_PRIVACY:
                let model = this.device.model()
                if (model === "MI 5s" || this.device.model() === "SM-G965U1" || this.device.model() === "ASUS_Z012D") {
                    await this.tapForDevice(this.device, 530, 970)
                } else if (model === "HUAWEI NXT-TL00") {
                    await this.tapForDevice(this.device, 530, 950)
                } else if (this.device.model() === "ONEPLUS A6000") {
                    await this.tapForDevice(this.device, 530, 1064)
                } else if (this.device.model() === "moto g51 5G") {
                    await this.tapForDevice(this.device, 500, 1080)
                } else if (this.device.model() === "CPH2469") {
                    await this.tapForDevice(this.device, 360, 820)
                } else if (this.device.model() === "M2006C3LG" || this.device.model() === "220233L2G") {
                    await this.tapForDevice(this.device, 350, 777)
                } else if (this.device.model() === "KB2003" || this.device.model() === "DE2117") {
                    await this.tapForDevice(this.device, 500, 1200)
                    await this.tapForDevice(this.device, 500, 1120)
                } else
                    try {
                        await this.tapForDevice(this.device, 500, 1120)
                        // await this.device.shell("input tap " + 500 + " " + 1120)
                    } catch (e) {
                        console.log(e)
                    }
                await delay(2000);
                await this.checkResultWithOcr();
                break;
            case OCRResult.BRAVE_PRIVACY_PUB:
                if (this.device.model() === "MI 5s" || this.device.model() === "ASUS_Z012D") {
                    await this.tapForDevice(this.device, 60, 1400)
                } else if (this.device.model() === "HUAWEI NXT-TL00") {
                    await this.tapForDevice(this.device, 530, 950)
                } else
                    await this.tapForDevice(this.device, 455, 1920)
                await delay(2000);
                await this.checkResultWithOcr();
                break;
            case OCRResult.BRAVE_NOTIFICATION:
                logWithDevice("BRAVE_NOTIFICATION", this.device)
                if (this.device.model() === "21091116C") {
                    await this.tapForDevice(this.device, 540, 1611)
                } else if (this.device.model() === "22041219PG") {
                    await this.tapForDevice(this.device, 530, 1600)
                } else if (this.device.model() === "CPH2469") {
                    await this.tapForDevice(this.device, 322, 1146)
                } else
                    await this.tapForDevice(this.device, 500, 1680)
                // await this.device.shell("input tap " + 500 + " " + 1680)
                await delay(2000);
                await this.checkResultWithOcr();
                break;
            case OCRResult.CHOOSE_POSITION:
                logWithDevice("CHOOSE_POSITION", this.device)
                await this.tapForDevice(this.device, 300, 1300)
                await delay(2000);
                await this.tapForDevice(this.device, 300, 1700)
                await this.checkResultWithOcr();
                break;
            case OCRResult.BRAVE_VPN_SKIP:
                logWithDevice("BRAVE_VPN_SKIP", this.device)
                if (this.device.model() === "M2006C3LG") {
                    await this.tapForDevice(this.device, 580, 445)
                }
                break;
            case OCRResult.TO_SKIP
            :
                logWithDevice("TO_SKIP", this.device)
                if (this.device.model() === "21091116C")
                    await this.device.shell("input tap " + 530 + " " + 1742)
                else
                    await this.device.shell("input tap " + 488 + " " + 1848)
                await delay(2000);
                break;
            case
            OCRResult.TO_REFRESH
            :
                await this.connect_to_browser(OCRResult.TO_REFRESH)
                break;
            case
            OCRResult.BLOCKED
            :
                await this.resetBrowser();
                break;
            case
            OCRResult.ONLINE_APPOINTMENT
            :
                await this.clickOnlineAppointment();
                break;
            case
            OCRResult.CONFIRM_RESEND_FORM
            :
                // logWithDevice("CONFIRM_RESEND_FORM", this.device)
                // await this.checkResultWithOcr();
                break;
            case
            OCRResult.PAGE_OPTIMIZATION
            :
                logWithDevice("PAGE_OPTIMIZATION", this.device)
                await this.skipOptimizationPage();
                await this.checkResultWithOcr();
                break;
            case
            OCRResult.GOOGLE_DISCONNECT
            :
                logWithDevice("GOOGLE_DISCONNECT", this.device)
                await this.tapGoogleDisconnectBtn()

                await this.checkResultWithOcr();
                break
            case
            OCRResult.NEED_TO_CLICK_LATE_BTN
            :
                await this.tapLaterBtn()
                this.firstStart = true;
                await this.loadPage()
                break;
            case
            OCRResult.CLOSED
            :
                await this.closePage();
                // this.isTerminated = true;
                break;
            default:
                await delay(5000);
                checkResult = this.ocrChecker.get_result();
        }

        if (checkResult

            ===
            undefined
        ) {
            await

                delay(
                    5000
                )
            ;
            checkResult = this.ocrChecker.get_result();
        }
    }


    async handleBraveSkipBtn() {
        let model = this.device.model()
        if (model === "CPH2219") {
            await this.device.shell("input tap " + 558 + " " + 1160)
        } else if (model === "MI 5s" || model === "ASUS_Z012D") {
            await this.tapForDevice(this.device, 530, 1000)
        } else if (model === "SM-G965U1") {
            await this.tapForDevice(this.device, 530, 1000)
        } else if (model === "HUAWEI NXT-TL00") {
            await this.tapForDevice(this.device, 530, 950)
        } else if (model === "M2006C3LG" || model === "220233L2G") {
            await this.tapForDevice(this.device, 360, 777)
        } else if (model === "ONEPLUS A6000") {
            await this.tapForDevice(this.device, 530, 1045)
        } else if (model === "CPH2469") {
            await this.tapForDevice(this.device, 360, 820)
        } else {
            logWithDevice("handleBraveSkipBtn", this.device)
            await this.tapForDevice(this.device, 470, 1160)
        }
        await delay(2000);
        await this.tapForDevice(this.device, 455, 1920)
        await delay(1000);
    }

    async tapForDevice(device, x, y) {
        let cmd = `adb -s ${device.serial()} shell input tap ${x} ${y}`
        logWithDevice("cmd is " + cmd, this.device)
        await exec(cmd);
    }

    async clickOnConfirmBtn() {
        if (this.device.model() === "CPH2219") {
            this.device.shell("input tap " + 900 + " " + 1532)
        } else if (this.device.model() === "MI 5s") {
            this.device.shell("input tap " + 925 + " " + 1325)
        } else if (this.device.model() === "22041219PG") {
            this.device.shell("input tap " + 925 + " " + 1430)
        } else if (this.device.model() === "moto g51 5G") {
            await this.tapForDevice(this.device, 950, 1434)
        } else
            this.device.shell("input tap " + 933 + " " + 1538)
        await delay(2000);
    }

    async closePage() {
        await this.resetBrowser();
    }

    async skipOptimizationPage() {
        logWithDevice("skipOptimizationPage", this.device)
        let model = this.device.model();
        if (model === "ASUS_X00QD") {
            this.device.shell("input tap " + 800 + " " + 2100)
            await delay(2000);
            this.device.shell("input tap " + 800 + " " + 2100)
            await delay(1000);
        } else if (model === "ONEPLUS A6000") {
            this.device.shell("input tap " + 818 + " " + 2140)
            await delay(2000);
            this.device.shell("input tap " + 818 + " " + 2140)
            await delay(1000);
        } else if (model === "CPH2219") {
            this.device.shell("input tap " + 772 + " " + 2146)
            await delay(2000);
            this.device.shell("input tap " + 772 + " " + 2146)
        } else if (model === "MI 5s") {
            this.device.shell("input tap " + 786 + " " + 1780)
            await delay(2000);
            this.device.shell("input tap " + 790 + " " + 1807)
            await delay(1000);
        } else {
            this.device.shell("input tap " + 800 + " " + 2215)
            await delay(2000);
            this.device.shell("input tap " + 800 + " " + 2215)
            await delay(1000);
        }
    }

    async tapLaterBtn() {
        logWithDevice("tapLaterBtn", this.device)
        let model = this.device.model();
        log("model is " + model);
        if (model === "CPH2219") {
            this.device.shell("input tap " + 385 + " " + 1930)
        } else if (model === "ASUS_X00QD") {
            this.device.shell("input tap " + 490 + " " + 1910)
        } else if (model === "Mi Note 10") {
            this.device.shell("input tap " + 550 + " " + 1920)
        } else if (model === "ONEPLUS A6000") {
            this.device.shell("input tap " + 535 + " " + 1945)
        } else if (model === "22041219PG") {
            this.device.shell("input tap " + 540 + " " + 1985)
        } else if (model === "21091116C") {
            this.device.shell("input tap " + 510 + " " + 1975)
        } else if (model === "MI 5s") {
            this.device.shell("input tap " + 510 + " " + 1615)
        } else
            this.device.shell("input tap " + 385 + " " + 2050)
        await delay(1000);
    }

    async enableDisableAirPlanMode() {
        logWithDevice("will enable/disable airplane mode", this.device)
        try {
            // await this.device.shell("cmd connectivity airplane-mode enable")
            await exceutShellCmd(this.device, "cmd connectivity airplane-mode enable")
            await delay(1000)
            await exceutShellCmd(this.device, "cmd connectivity airplane-mode disable")
            // await this.device.shell("cmd connectivity airplane-mode disable")
            await delay(2000)
        } catch (e) {
            try {
                await this.device.shell("cmd connectivity airplane-mode disable")
            } catch (e) {
                console.log(e)
            }
            console.log(e)
        }
    }

    async tapGoogleDisconnectBtn() {
        if (this.device.model() === "MI 5s") {
            if (this.browserPackageName.includes("brave")) {
                await this.tapForDevice(this.device, 535, 1629)
            } else
                await this.device.shell("input tap " + 550 + " " + 1740)
        } else {
            if (this.browserPackageName.includes("brave") && this.device.model() === "CPH2219") {
                await this.device.shell("input tap " + 411 + " " + 1977)
            } else if (this.browserPackageName.includes("brave") && this.device.model() === "RMX3151") {
                await this.device.shell("input tap " + 411 + " " + 1977)
            } else if (this.browserPackageName.includes("brave") && this.device.model() === "ONEPLUS A6000") {
                await this.device.shell("input tap " + 411 + " " + 1970)
            } else if (this.browserPackageName.includes("brave") && this.device.model() === "ASUS_X00QD") {
                await this.device.shell("input tap " + 411 + " " + 1970)
            } else if (this.browserPackageName.includes("brave") && this.device.model() === "22041219PG") {
                await this.tapForDevice(this.device, 411, 2020)
            } else if (this.browserPackageName.includes("brave") && this.device.model() === "21091116C") {
                await this.tapForDevice(this.device, 411, 2020)
            } else if (this.browserPackageName.includes("brave") && this.device.model() === "M2006C3LG") {//redmi 9a
                await this.tapForDevice(this.device, 411, 1300)
            } else if (this.browserPackageName.includes("brave") && this.device.model() === "220233L2G") {//redmi 9a
                await this.tapForDevice(this.device, 411, 1300)
            } else {
                await this.tapForDevice(this.device, 411, 2100)
            }
            // await this.device.shell("input tap " + 411 + " " + 2100)
        }
        await delay(2000);
    }

    async startPage(device, activity) {
        let cmd = `adb -s ${device.serial()} shell am start -n ${activity}`
        logWithDevice("cmd is " + cmd, this.device)
        await exec(cmd);
    }
}

module
    .exports = CommandorPage