const {_android: android} = require('playwright');
const ExcelUtil = require("./excel/ExcelUtil");
const CommandorPage = require("./workers/CommandorPage");
const {MongoManager, formatDate} = require("./workers/mongo_manager");
const alert = require('alert');
const schedule = require("node-schedule");

const mongoManager = new MongoManager();
const SEVEN_DAYS_IN_S = 3600 * 24 * 7;
const NINETY_DAYS_IN_S = 3600 * 24 * 30 * 3;
let excelUtil = new ExcelUtil();
let collectionName = formatDate(new Date())
// device_to_excludes = ["47e7e36b", "e30eb015"]
// device_to_excludes = ["J4AXB761H2322WJ"]
device_to_excludes = [ "R9TR608ZLKJ", "heuklr55wo8tfyvw"]
attributedPorts = []
const device_port_info = new Map();
startPort = 9000

async function filterAlreadyBookedContacts(contactList) {
    let alreadyBookedContacts = await mongoManager.getAllSuccessfulItemsForDay(collectionName);
    let contactsToBook = [];
    contactList.forEach((contact) => {
        if (alreadyBookedContacts.find((bookedContact) => bookedContact.email === contact.mail) === undefined) {
            contactsToBook.push(contact)
        }
    })
    return contactsToBook;
}

async function filterAlreadyAcceptedContacts(contactList) {
    let alreadyBookedContacts = await mongoManager.getAllAcceptedAppointments();
    let contactsToBook = [];
    contactList.forEach((contact) => {
        let needToBook = true;
        alreadyBookedContacts.forEach((acceptedItem) => {
            if (acceptedItem.email === contact.mail) {
                console.log("=====handle already accepted item before booking====");
                console.log("accepted_at is " + acceptedItem.accepted_at);
                console.log("accepted email is " + acceptedItem.email);
                needToBook = acceptedItem.accepted_at + NINETY_DAYS_IN_S <= (new Date()) / 1000;
                if (!needToBook) {
                    console.log("already accepted appointment --> skip");
                }
            }
        })
        if (needToBook) {
            contactsToBook.push(contact)
        }
    })
    return contactsToBook;
}

async function filterBlacklistedContacts(contactList) {
    let blackListedContacts = await mongoManager.getAllBlackedListItems();
    let contactsToBook = [];
    contactList.forEach((contact) => {
        let needToBook = true;
        blackListedContacts.forEach((blackListItem) => {
            if (blackListItem.mail === contact.mail) {
                console.log("=====handle blacklist item before booking=====");
                console.log("update_at_s is " + blackListItem.update_at_in_s)
                console.log("black list email is " + blackListItem.mail)
                needToBook = blackListItem.update_at_in_s + SEVEN_DAYS_IN_S <= (new Date()) / 1000;
                if (!needToBook) {
                    console.log("contact in blacklist -->skip");
                }
            }
        })
        if (needToBook) {
            contactsToBook.push(contact)
        }
    })
    return contactsToBook;
}

async function needToBook(contact, mongoManager) {
    console.log("check contact with email " + contact.mail)
    let alreadyBooked = await mongoManager.getAllSuccessfulItemsForDay(collectionName);
    let blackListItems = await mongoManager.getAllBlackedListItems();
    let alreadyAcceptedItems = await mongoManager.getAllAcceptedAppointments();
    let needToBook = true;
    await alreadyBooked.forEach((bookedItem) => {
            if (bookedItem.email === contact.mail) {
                needToBook = false;
            }
        }
    )
    if (needToBook) {
        await blackListItems.forEach((blackListItem) => {
            if (blackListItem.mail === contact.mail) {
                console.log("=====handle blacklist item====");
                console.log("update_at_s is " + blackListItem.update_at_in_s)
                console.log("black list email is " + blackListItem.mail)
                needToBook = blackListItem.update_at_in_s + SEVEN_DAYS_IN_S <= (new Date()) / 1000;
                if (!needToBook) {
                    console.log("contact in blacklist -->skip");
                }
            }
        })
    }
    if (needToBook) {
        await alreadyAcceptedItems.forEach((acceptedItem) => {
            if (acceptedItem.email === contact.mail) {
                console.log("=====handle already accepted item====");
                console.log("accepted_at is " + acceptedItem.accepted_at);
                console.log("accepted email is " + acceptedItem.email);
                needToBook = acceptedItem.accepted_at + NINETY_DAYS_IN_S <= (new Date()) / 1000;
                if (!needToBook) {
                    console.log("already accepted appointment --> skip");
                }
            }
        })
    }
    return needToBook
}

async function startBook(contactPojo, device, selectedStore, audioAnalyse, alertBeep, port) {
    console.log(`model: ${device.model()}`);
    console.log(`serial: ${device.serial()}`);
    if (await needToBook(contactPojo, mongoManager)) {
        let commandor = new CommandorPage(contactPojo, device, mongoManager, selectedStore, audioAnalyse, alertBeep, port);
        //read contacts form excel
        return await commandor.loadPage();
    } else {
        console.log("do not send request --> skip")
    }
}

async function startWithList(contacts, device, selectedStore, audioAnalyse, alertBeep, port) {
    let duplicatedList = [].concat(contacts).concat(contacts).concat(contacts)
    // let duplicatedList = [].concat(contacts)
    await duplicatedList.reduce(async (promise, contactPojo) => {
        // This line will wait for the last async function to finish.
        // The first iteration uses an already resolved Promise
        // so, it will immediately continue.
        await promise;
        const contents = await startBook(contactPojo, device, selectedStore, audioAnalyse, alertBeep, port);
        console.log(contents);
    }, Promise.resolve());
}

async function scheduleBookWithNumbers(startNumber, endNumber, selectedStore, pathToExcelFile = '/Users/lpan/Desktop/contact_all.xlsx', audioAnalyse = true, alertBeep = false) {
    console.log("scheduleBookWithNumbers() called")
    alert("定时成功")
    schedule.scheduleJob('30 10 * * *', function () {
        console.log("start startBookWithNumbers")
        startBookWithNumbers(startNumber, endNumber, selectedStore, pathToExcelFile, audioAnalyse, alertBeep)
    })
}

function shuffle(array) {
    let currentIndex = array.length, randomIndex;
    // While there remain elements to shuffle.
    while (currentIndex !== 0) {
        // Pick a remaining element.
        randomIndex = Math.floor(Math.random() * currentIndex);
        currentIndex--;
        // And swap it with the current element.
        [array[currentIndex], array[randomIndex]] = [
            array[randomIndex], array[currentIndex]];
    }

    return array;
}

async function startBookWithNumbers(startNumber, endNumber, selectedStore, pathToExcelFile = '/Users/lpan/Desktop/contact_all.xlsx', audioAnalyse = true, alertBeep = false) {
    console.log("startBookWithNumbers() called, with alertBeep:" + alertBeep)
    console.log("startBookWithNumbers() called, with audioAnalyse:" + audioAnalyse)
    let allContactList = excelUtil.readContacts(pathToExcelFile);
    let contactList;
    if (endNumber <= allContactList.length) {
        contactList = allContactList.slice(startNumber, endNumber);
    } else {
        contactList = allContactList.slice(startNumber, allContactList.length);
    }
    if (contactList.length === 0) {
        alert("联系人数为0")
        return
    }

    function startForwordingForDevice(device) {
        const execSync = require('child_process').execSync;
        // get attributed port
        let attributedPort = device_port_info[device.serial()]
        if (attributedPort) {
            let cmd = 'adb -s ' + device.serial() + " forward tcp:" + attributedPort + " localabstract:chrome_devtools_remote";
            console.log("cmd is " + cmd);
            const output = execSync(cmd, {encoding: 'utf-8'});  // the default is 'buffer'
            console.log('Output was:\n', output);
        } else {
            attributedPort = startPort;
            startPort++;
            let cmd = 'adb -s ' + device.serial() + " forward tcp:" + attributedPort + " localabstract:chrome_devtools_remote";
            console.log("cmd is " + cmd);
            const output = execSync(cmd, {encoding: 'utf-8'});  // the default is 'buffer'
            console.log('Output was:\n', output);
        }
        // start chrome
        // device.shell("am start -n com.brave.browser/com.google.android.apps.chrome.Main")
        return attributedPort
    }

    mongoManager.connect().then(r => {
        filterAlreadyBookedContacts(contactList).then((listToBook) => {
            filterAlreadyAcceptedContacts(listToBook).then(notAcceptedContacts => {
                    filterBlacklistedContacts(notAcceptedContacts).then(listWithoutBlackContact => {

                        console.log("number of contacts to book:" + listWithoutBlackContact.length)
                        android.devices().then((devices) => {
                            if (devices.length === 0) {
                                alert("未找到连接的设备");
                                return
                            }
                            let filteredDeviceList = devices.filter(device => !device_to_excludes.includes(device.serial()))
                            let segmentNumber = listWithoutBlackContact.length / filteredDeviceList.length;
                            console.log("connected device number:" + filteredDeviceList.length)
                            console.log("segmentNumber:" + segmentNumber)
                            for (let i = 0; i < filteredDeviceList.length; i++) {
                                let device = filteredDeviceList[i];
                                let port = startForwordingForDevice(device)
                                startWithList(listWithoutBlackContact.slice(i * segmentNumber, segmentNumber * (i + 1)), device, selectedStore, audioAnalyse, alertBeep, port);
                            }
                        })
                    })
                }
            )
        })
    });
}

module.exports = {startBookWithNumbers, scheduleBookWithNumbers}