const WAITING_FOR_REVIEW_IN_HOURS = 12;
const RUNNING_OUT_OF_TIME_IN_HOURS = 6;

const CLAIMED = 1;
const SUBMITTED = 4;
const WAITING_FOR_GOOGLE_REVIEW_OF_PARENTAL_CONSENT = 8;

const ACTION = 1;
const COMMENT = 2;

const ALL = -1;

let cache = {};

const tasksToIgnore = [];
const tasksHandled = [];
const tasksToHighlight = [];
const tasksRunningOutOfTime = [];

let waitUntilUserIsOnInProgressTab = setInterval(() => {
    if (isOnInProgressSite()) {
        clearInterval(waitUntilUserIsOnInProgressTab);
        main();
    }
}, 200);

async function main() {
    const taskPromises = [];
    await loadCache(await fetch(`https://codein.withgoogle.com/api/program/2017/taskinstance/?is_active=True&my_tasks=false&order=-last_update_by_student&page=1&page_size=100`));
    for (let {task} of Object.values(cache)) {
        if (task.status === WAITING_FOR_GOOGLE_REVIEW_OF_PARENTAL_CONSENT
            || (task.status === CLAIMED && task.comments_count === 0)) {
            tasksToIgnore.push(task);
        } else if (task.last_update_by_student) {
            taskPromises.push(handleLastUpdateByStudent(task));
        } else {
            taskPromises.push(handleLastUpdateByMentor(task));
        }
    }
    Promise.all(taskPromises).then(saveCache);
}

async function handleLastUpdateByMentor(task) {
    if (isRunningOutOfTime(task)) {
        tasksRunningOutOfTime.push(task);
    } else {
        tasksHandled.push(task);
    }
}

async function handleLastUpdateByStudent(task) {
    const taskDetails = await getLast10TaskDetails(task.id);
    if (isRunningOutOfTime(task)) {
        tasksRunningOutOfTime.push(task);
    } else if (isWaitingForComment(task, taskDetails) || isWaitingForReview(task, taskDetails)) {
        tasksToHighlight.push(task);
    }
}

function isWaitingForComment(task, taskDetails) {
    return task.last_update_by_student && taskDetails[0].kind === COMMENT;
}

function isRunningOutOfTime(task) {
    return !task.deadline_paused && differenceInHours(task.deadline_to_complete) < RUNNING_OUT_OF_TIME_IN_HOURS;
}

function isWaitingForReview(task, taskDetails) {
    if (task.status === SUBMITTED) {
        for (let task_detail of taskDetails) {
            if (task_detail.old_task_instance_status !== SUBMITTED && task_detail.new_task_instance_status === SUBMITTED) {
                return differenceInHours(task_detail.created) >= WAITING_FOR_REVIEW_IN_HOURS;
            }
        }
    }
    return false;
}

async function getLast10TaskDetails(taskId) {
    if (cache[taskId].taskDetails) {
        return cache[taskId].taskDetails;
    } else {
        const taskDetailsCount = 10;
        const taskDetails = await fetch(`https://codein.withgoogle.com/api/program/current/taskupdate/?page=1&page_size=${taskDetailsCount}&task_instance=${taskId}`, taskDetailsCount);
        cache[taskId].taskDetails = taskDetails;
        return taskDetails;
    }
}

/*
 * Utils methods
 */
async function fetch(url, count = ALL) {
    const items = [];
    while (true) {
        const {results, next} = await $.getJSON(url);
        items.push(...results);
        if (next && (count === ALL || items.length < count)) {
            url = next;
        } else {
            break;
        }
    }
    return (count > 0) ? items.slice(0, count) : items;
}

function isOnInProgressSite() {
    return location.pathname.match(/dashboard\/task-instances\/in-progress/);
}

function differenceInHours(from, to = new Date()) {
    return Math.abs(new Date(from) - to) / 36e5;
}

function findTaskWithId(tasks, id) {
    for (let task of tasks) {
        if (task.id === id) {
            return task;
        }
    }
    return null;
}

function hasChromeLocalStorage() {
    return !!window.chrome && window.chrome.storage && window.chrome.storage.local;
}

/*
 * Caching
 */
function loadCache(tasks) {
    return new Promise((resolve, reject) => {
        if (hasChromeLocalStorage()) {
            chrome.storage.local.get(null, (cachedEntries) => {
                for (let task of tasks) {
                    const cachedTask = (cachedEntries[task.id] || {}).task;
                    if (cachedTask
                        && cachedTask.modified === task.modified
                        && cachedTask.status === task.status
                        && cachedTask.comments_count === task.comments_count
                    ) {
                        cache[task.id] = cachedEntries[task.id];
                    } else {
                        cache[task.id] = {task};
                    }
                }
                resolve();
            });
        } else {
            for (let task of tasks) {
                cache[task.id] = {task};
            }
            resolve();
        }
    });
}

function saveCache() {
    return new Promise((resolve, reject) => {
        if (hasChromeLocalStorage()) {
            chrome.storage.local.clear();
            chrome.storage.local.set(cache);
            resolve();
        } else {
            resolve();
        }
    });
}

/*
 * Colorize tasks
 */
setInterval(() => {
    if (isOnInProgressSite()) {
        tasksToIgnore.forEach((task) => setStyle(task.id, {'opacity': '0.5'}));
        tasksHandled.forEach((task) => setStyle(task.id, {'color': '#9ccc00'}));
        tasksToHighlight.forEach((task) => setStyle(task.id, {'color': '#e53935'}));
        tasksRunningOutOfTime.forEach((task) => setStyle(task.id, {'color': '#2894ed'}));
    }
}, 2000);

function setStyle(taskId, css) {
    $('a[href*="' + taskId + '"]').css(css);
}