/* eslint no-invalid-this: "off" */

import Nightmare from 'nightmare';
import {URL} from 'url';
import config from './config.js';

let currentUser;

let actions = {
    clearTextarea: function(selector, done) {
        this.wait(selector)
            .evaluate(inputSelector => {
                return document.querySelector(inputSelector).value = '';
            }, selector)
            .then(done)
            .catch(done);
    },

    clearInput: function(selector, done) {
        this.wait(selector)
            .evaluate(inputSelector => {
                return document.querySelector(inputSelector).closest('*[model], *[field], *[value]').$ctrl.value = '';
            }, selector)
            .then(done)
            .catch(done);
    },

    login: function(userName, done) {
        if (currentUser)
            this.waitToClick('#logout');

        let doLogin = () => {
            this.wait(`vn-login input[name=user]`)
                .write(`vn-login input[name=user]`, userName)
                .write(`vn-login input[name=password]`, 'nightmare')
                .click(`vn-login input[type=submit]`)
                .then(() => {
                    currentUser = userName;
                    done();
                })
                .catch(done);
        };

        this.waitForURL('#!/login')
            .then(doLogin)
            .catch(() => {
                this.goto(`${config.url}/#!/login`)
                    .then(doLogin)
                    .catch(done);
            });
    },

    waitForLogin: function(userName, done) {
        if (currentUser === userName) {
            return this.waitToClick('vn-topbar a[ui-sref="home"]')
                .waitForURL('#!/')
                .changeLanguageToEnglish()
                .then(done)
                .catch(done);
        }
        return this.login(userName)
            .waitForURL('#!/')
            .url()
            .changeLanguageToEnglish()
            .then(done)
            .catch(done);
    },

    resetLogin: function(done) {
        this.then(() => {
            currentUser = undefined;
            done();
        })
            .catch(done);
    },

    changeLanguageToEnglish: function(done) {
        let langSelector = 'vn-user-configuration-popover vn-autocomplete[field="$ctrl.lang"]';

        this.waitToClick('#user')
            .wait(langSelector)
            .waitToGetProperty(`${langSelector} input`, 'value')
            .then(lang => {
                if (lang === 'English') {
                    this.then(done)
                        .catch(done);
                } else {
                    this.autocompleteSearch(langSelector, 'English')
                        .then(done)
                        .catch(done);
                }
            });
    },

    selectModule: function(moduleName, done) {
        let snakeName = moduleName.replace(/[\w]([A-Z])/g, m => {
            return m[0] + '-' + m[1];
        }).toLowerCase();
        this.waitToClick(`vn-home a[ui-sref="${moduleName}.index"]`)
            .waitForURL(snakeName)
            .then(done)
            .catch(done);
    },

    loginAndModule: function(userName, moduleName, done) {
        this.waitForLogin(userName)
            .selectModule(moduleName)
            .then(done)
            .catch(done);
    },

    parsedUrl: function(done) {
        this.url()
            .then(url => {
                done(null, new URL(url));
            }).catch(done);
    },

    getProperty: function(selector, property, done) {
        this.evaluate_now((selector, property) => {
            return document.querySelector(selector)[property].replace(/\s+/g, ' ').trim();
        }, done, selector, property);
    },

    waitPropertyLength: function(selector, property, minLength, done) {
        this.wait((selector, property, minLength) => {
            const element = document.querySelector(selector);
            return element && element[property] != null && element[property] !== '' && element[property].length >= minLength;
        }, selector, property, minLength)
            .getProperty(selector, property)
            .then(result => done(null, result), done);
    },

    waitPropertyValue: function(selector, property, status, done) {
        this.wait(selector)
            .wait((selector, property, status) => {
                const element = document.querySelector(selector);
                return element[property] === status;
            }, selector, property, status)
            .then(done)
            .catch(done);
    },

    waitToGetProperty: function(selector, property, done) {
        this.wait((selector, property) => {
            const element = document.querySelector(selector);
            return element && element[property] != null && element[property] !== '';
        }, selector, property)
            .getProperty(selector, property)
            .then(result => done(null, result), done);
    },

    write: function(selector, text, done) {
        this.wait(selector)
            .type(selector, text)
            .then(done)
            .catch(done);
    },

    waitToClick: function(selector, done) {
        this.wait(selector)
            .click(selector)
            .then(done)
            .catch(done);
    },

    waitToFocus: function(selector, done) {
        this.wait(selector)
            .evaluate_now(selector => {
                let element = document.querySelector(selector);
                element.focus();
            }, done, selector)
            .then(done)
            .catch(done);
    },

    isVisible: function(selector, done) {
        this.wait(selector)
            .evaluate_now(elementSelector => {
                const selectorMatches = document.querySelectorAll(elementSelector);
                const element = selectorMatches[0];
                if (selectorMatches.length > 1)
                    throw new Error(`multiple matches of ${elementSelector} found`);

                let isVisible = false;
                if (element) {
                    const eventHandler = event => {
                        event.preventDefault();
                        isVisible = true;
                    };
                    element.addEventListener('mouseover', eventHandler);
                    const elementBoundaries = element.getBoundingClientRect();
                    const x = elementBoundaries.left + element.offsetWidth / 2;
                    const y = elementBoundaries.top + element.offsetHeight / 2;
                    const elementInCenter = document.elementFromPoint(x, y);
                    const elementInTopLeft = document.elementFromPoint(elementBoundaries.left, elementBoundaries.top);
                    const elementInBottomRight = document.elementFromPoint(elementBoundaries.right, elementBoundaries.bottom);
                    const e = new MouseEvent('mouseover', {
                        view: window,
                        bubbles: true,
                        cancelable: true,
                    });
                    if (elementInCenter)
                        elementInCenter.dispatchEvent(e);

                    if (elementInTopLeft)
                        elementInTopLeft.dispatchEvent(e);

                    if (elementInBottomRight)
                        elementInBottomRight.dispatchEvent(e);

                    element.removeEventListener('mouseover', eventHandler);
                }
                return isVisible;
            }, done, selector);
    },

    waitImgLoad: function(selector, done) {
        this.wait(selector)
            .wait(selector => {
                const imageReady = document.querySelector(selector).complete;
                return imageReady;
            }, selector)
            .then(done)
            .catch(() => {
                done(new Error(`image ${selector}, load timed out`));
            });
    },

    clickIfVisible: function(selector, done) {
        this.wait(selector)
            .isVisible(selector)
            .then(visible => {
                if (visible)
                    return this.click(selector);

                throw new Error(`invisible selector: ${selector}`);
            })
            .then(done)
            .catch(done);
    },

    countElement: function(selector, done) {
        this.evaluate_now(selector => {
            return document.querySelectorAll(selector).length;
        }, done, selector);
    },

    waitForNumberOfElements: function(selector, count, done) {
        this.wait((selector, count) => {
            return document.querySelectorAll(selector).length === count;
        }, selector, count)
            .then(done)
            .catch(() => {
                done(new Error(`.waitForNumberOfElements() for ${selector}, count ${count} timed out`));
            });
    },

    waitForClassNotPresent: function(selector, className, done) {
        this.wait(selector)
            .wait((selector, className) => {
                if (!document.querySelector(selector).classList.contains(className))
                    return true;
            }, selector, className)
            .then(done)
            .catch(() => {
                done(new Error(`.waitForClassNotPresent() for ${selector}, class ${className} timed out`));
            });
    },

    waitForClassPresent: function(selector, className, done) {
        this.wait(selector)
            .wait((selector, className) => {
                if (document.querySelector(selector).classList.contains(className))
                    return true;
            }, selector, className)
            .then(done)
            .catch(() => {
                done(new Error(`.waitForClassPresent() for ${selector}, class ${className} timed out`));
            });
    },

    waitForTextInElement: function(selector, text, done) {
        this.wait(selector)
            .wait((selector, text) => {
                return document.querySelector(selector).innerText.toLowerCase().includes(text.toLowerCase());
            }, selector, text)
            .then(done)
            .catch(done);
    },

    waitForTextInInput: function(selector, text, done) {
        this.wait(selector)
            .wait((selector, text) => {
                return document.querySelector(selector).value.toLowerCase().includes(text.toLowerCase());
            }, selector, text)
            .then(done)
            .catch(done);
    },

    waitForInnerText: function(selector, done) {
        this.wait(selector)
            .wait(selector => {
                const innerText = document.querySelector(selector).innerText;
                return innerText != null && innerText != '';
            }, selector)
            .evaluate_now(selector => {
                return document.querySelector(selector).innerText;
            }, done, selector);
    },

    waitForEmptyInnerText: function(selector, done) {
        this.wait(selector => {
            return document.querySelector(selector).innerText == '';
        }, selector)
            .then(done)
            .catch(done);
    },

    waitForURL: function(hashURL, done) {
        this.wait(hash => {
            return document.location.hash.includes(hash);
        }, hashURL)
            .then(done)
            .catch(done);
    },

    waitForShapes: function(selector, done) {
        this.wait(selector)
            .evaluate_now(selector => {
                const shapes = document.querySelectorAll(selector);
                const shapesList = [];

                for (const shape of shapes)
                    shapesList.push(shape.innerText);


                return shapesList;
            }, done, selector);
    },
    waitForSnackbar: function(done) {
        this.wait(500).waitForShapes('vn-snackbar .shape .text')
            .then(shapes => {
                done(null, shapes);
            }).catch(done);
    },

    waitForLastShape: function(selector, done) {
        this.wait(selector)
            .evaluate_now(selector => {
                const shape = document.querySelector(selector);

                return shape.innerText;
            }, done, selector);
    },

    waitForLastSnackbar: function(done) {
        this.wait(500).waitForLastShape('vn-snackbar .shape .text')
            .then(shapes => {
                done(null, shapes);
            }).catch(done);
    },

    accessToSearchResult: function(searchValue, done) {
        this.clearInput('vn-searchbar input')
            .write('vn-searchbar input', searchValue)
            .click('vn-searchbar vn-icon[icon="search"]')
            .wait(100)
            .waitForNumberOfElements('.searchResult', 1)
            .evaluate(() => {
                return document.querySelector('ui-view vn-card vn-table') != null;
            })
            .then(result => {
                if (result)
                    return this.waitToClick('ui-view vn-card vn-td');

                return this.waitToClick('ui-view vn-card a');
            })
            .then(done)
            .catch(done);
    },

    accessToSection: function(sectionRoute, done) {
        this.wait(`vn-left-menu`)
            .evaluate(sectionRoute => {
                return document.querySelector(`vn-left-menu ul li ul li > a[ui-sref="${sectionRoute}"]`) != null;
            }, sectionRoute)
            .then(nested => {
                if (!nested)
                    return this.waitToClick(`vn-left-menu li > a[ui-sref="${sectionRoute}"]`);

                return this.waitToClick('vn-left-menu .collapsed')
                    .wait('vn-left-menu .expanded')
                    .waitToClick(`vn-left-menu li > a[ui-sref="${sectionRoute}"]`);
            })
            .then(done)
            .catch(done);
    },

    autocompleteSearch: function(autocompleteSelector, searchValue, done) {
        this.wait(autocompleteSelector)
            .waitToClick(`${autocompleteSelector} input`)
            .write(`${autocompleteSelector} vn-drop-down input`, searchValue)
            .waitToClick(`${autocompleteSelector} li.active`)
            .wait((autocompleteSelector, searchValue) => {
                return document.querySelector(`${autocompleteSelector} input`).value.toLowerCase().includes(searchValue.toLowerCase());
            }, autocompleteSelector, searchValue)
            .then(done)
            .catch(() => {
                done(new Error(`.autocompleteSearch() for ${autocompleteSelector}, timed out`));
            });
    },

    datePicker: function(datePickerSelector, changeMonth, day, done) {
        this.wait(datePickerSelector)
            .mousedown(datePickerSelector)
            .wait('div.flatpickr-calendar.open');
        if (changeMonth > 0)
            this.mousedown('body > div.flatpickr-calendar.open > div.flatpickr-months > span.flatpickr-next-month > svg');


        if (changeMonth < 0)
            this.mousedown('body > div.flatpickr-calendar.open > div.flatpickr-months > span.flatpickr-prev-month > svg');

        let daySelector;

        if (!day)
            daySelector = 'div.flatpickr-calendar.open span.flatpickr-day:nth-child(16)';

        if (day)
            daySelector = `span.flatpickr-day[aria-label~="${day},"]:not(.prevMonthDay):not(.nextMonthDay)`;


        this.wait(selector => {
            return document.querySelector(selector);
        }, daySelector)
            .evaluate(selector => {
                let event = new MouseEvent('mousedown', {
                    bubbles: true,
                    cancelable: true,
                    view: window
                });
                document.querySelector(selector).dispatchEvent(event);
            }, daySelector)
            .then(done)
            .catch(() => {
                done(new Error(`.datePicker(), for ${daySelector} timed out`));
            });
    },

    reloadSection: function(sectionRoute, done) {
        this.waitToClick('vn-icon[icon="desktop_windows"]')
            .wait('vn-card.summary')
            .waitToClick(`vn-left-menu li > a[ui-sref="${sectionRoute}"]`)
            .then(done)
            .catch(done);
    },

    forceReloadSection: function(sectionRoute, done) {
        this.waitToClick('vn-icon[icon="desktop_windows"]')
            .waitToClick('button[response="ACCEPT"]')
            .wait('vn-card.summary')
            .waitToClick(`vn-left-menu li > a[ui-sref="${sectionRoute}"]`)
            .then(done)
            .catch(done);
    },

    checkboxState: function(selector, done) {
        this.wait(selector)
            .evaluate(checkboxSelector => {
                return document.querySelector(checkboxSelector).getAttribute('class');
            }, selector)
            .then(elementClass => {
                let classList = elementClass.split(' ');
                let className;

                if (classList.includes('md-checked'))
                    className = 'checked';
                if (classList.includes('md-intermediate'))
                    className = 'intermediate';
                if (!classList.includes('md-intermediate') && !classList.includes('md-checked'))
                    className = 'unchecked';

                if (!className)
                    throw new Error(`selector: ${selector} is not a md-checkbox`);

                done(null, className);
            })
            .then(done)
            .catch(done);
    }
};

Object.keys(actions).forEach(function(name) {
    let fn = actions[name];
    Nightmare.action(name, fn);
});