import { FlashMessenger, Helper, InitPromise, Translator, MenuAction, AbstractSite, Fragment, ScriptLoader, Menu } from './pwa-lib.js';
import { DataManager, SettingsManager, Form, SettingsSite, LocalStorageSettingsFragment, Constants, DataSmartColumn, ImgConstSmartColumn, ListHelper, SmartColumn } from './pwa-core.js';

class UserManager {
    static init(app) {
        UserManager.getMeUrl = null;
        UserManager.userData = {
            online: false,
            id: null,
            accesses: ["default"]
        };
        UserManager.app = app;

        UserManager.fetchMePromise = new Promise(function (resolve) {
            UserManager.fetchMePromiseResolver = resolve;
        });
    }

    static setData(data) {
        UserManager.userData = Object.assign(UserManager.userData, data);
        let siteManager = UserManager.app.getSiteManager();

        if (siteManager)
            siteManager.redrawCurrentActionBar();
    }

    static fetchMe(url) {
        UserManager.getMeUrl = Helper.nonNull(url, UserManager.getMeUrl);
        return DataManager.load(UserManager.getMeUrl).then(function (result) {
            if (result["success"]) {
                UserManager.setData(result["result"]);
            }
            UserManager.fetchMePromiseResolver();
        });
    }

    static logOut() {
        return DataManager.load("u/logout").then(function (data) {
            if (data["success"]) {
                UserManager.setData(data["result"]);
                let siteManager = UserManager.app.getSiteManager();

                if (siteManager)
                    siteManager.refreshCurrentSite();
                FlashMessenger.addMessage(FlashMessenger.MESSAGE_TYPE_SUCCESS, Translator.translate("logged-out-successfully"));
            }
        });
    }

    static hasAccess(access) {
        // console.log("Has access", access, UserManager.userData["accesses"].indexOf(access), UserManager.userData);
        return (UserManager.userData["accesses"].indexOf(access) >= 0)
    }

    static addCurrentUserListener(userId, listener) {
        UserManager.addIsLoggedInListener(function (isLoggedIn) {
            listener(isLoggedIn && UserManager.isCurrentUser(userId));
        });
    }

    static addIsLoggedInListener(listener) {
        this.fetchMePromise.then(function () {
            listener(UserManager.isLoggedIn());
        });
    }

    static isCurrentUser(userId) {
        return UserManager.userData.id === userId;
    }

    static isLoggedIn() {
        return Helper.isNotNull(UserManager.userData) && Helper.isNotNull(UserManager.userData.id);
    }
}

InitPromise.addPromise(function(app){
    UserManager.init(app);
    return UserManager.fetchMe("u/me").then(function(){
        UserManager.addIsLoggedInListener(function (isLoggedIn) {
            if (isLoggedIn) {
                const settingsManager = SettingsManager.getInstance();
                const settings = Helper.cloneJson(settingsManager.getSettings());
                for (let k in settings) {
                    settings[k]["value"] = JSON.stringify(settings[k]["value"]);
                }
                DataManager.send("u/syncSettings", settings).then(function(res){
                    if (res["success"])
                    {
                        for (let k in res["result"])
                        {
                            res["result"][k]["value"] = JSON.parse(res["result"][k]["value"]);
                        }
                        settingsManager.setSettings(res["result"]);
                    }
                });
            }
        });
    });
});

class UserAction extends MenuAction {
    constructor(title, callback, icon, order, access) {
        super(title, callback, icon, order);
        this._access = Helper.nonNull(access, "default");
    }

    getVisible() {
        // console.log("Action-access: ", this._access);
        return (super.getVisible() && UserManager.hasAccess(this._access));
    }

    getAccess() {
        return this._access;
    }

    copy(instance){
        let copy = super.copy(Helper.nonNull(instance, new UserAction()));
        copy._access = this._access;
        return copy;
    }
}

class NotAllowedSite extends AbstractSite{
    constructor(siteManager) {
        super(siteManager, 'userManagement/html/403.html');
    }
}

class UserSite extends AbstractSite {

    constructor(siteManager, view, deepLink, access) {
        super(siteManager, view, deepLink);
        this._access = access;
    }

    onConstruct(args) {
        if (!UserManager.hasAccess(this._access))
        {
            this.startSite(NotAllowedSite);
            this.finish({
                "error":403 
            });
            return;
        }
        return super.onConstruct(args);
    }

    onStart(args) {
        if (!UserManager.hasAccess(this._access))
        {
            this.startSite(NotAllowedSite);
            this.finish({
                "error":403
            });
            return;
        }
        return super.onStart(args);
    }
}

class LoginForm extends Form {

    constructor(formElem, url, method, isCachable) {
        super(formElem, url, method, isCachable);

        let emailElem = formElem.querySelector("#email");
        let passwordElem = formElem.querySelector("#password");

        let listener = function(){
            emailElem.setCustomValidity("");
            passwordElem.setCustomValidity("");
        };

        emailElem.addEventListener("keydown", listener);
        passwordElem.addEventListener("keydown", listener);
    }
}

class RegistrationForm extends Form {
    constructor(formElem, url, method, isCachable) {
        super(formElem, url, method, isCachable);

        // this.pw1 = formElem.querySelector("#password1");
        // this.pw2 = formElem.querySelector("#password2");

        // let self=this;
        // this.pw1.addEventListener("change", function(){
        //     self.checkPw();
        // });
        // this.pw2.addEventListener("change", function(){
        //     self.checkPw();
        // });
    }

    checkPw(){
        // if (this.pw1.value !== this.pw2.value || this.pw1.value.length < 8)
        // {
        //
        // }
    }
}

class UserFragment extends Fragment{

    constructor(site, view, access) {
        super(site, view);
        this._access = access;
    }

    isActive() {
        return super.isActive() && UserManager.hasAccess(this._access);
    }
}

class PasswordSettingsFragment extends UserFragment{
    constructor(site)
    {
        super(site, "userManagement/html/fragments/passwordSettings.html", "online");
    }
    onFirstStart() {
        let res = super.onFirstStart();
        let form = new Form(document.getElementById("change-password-form"), "u/passwordSettings/set", "post");
        form.onSubmit(function(res){
            for (let i = 0, n = res.length; i < n; i++)
            {
                FlashMessenger.addMessage(res[i]);
            }
            form.setValues({
                "oldPassword":"",
                "newPassword1":"",
                "newPassword2":""
            });
        });
        return res;
    }
}
InitPromise.addPromise(function(){
    SettingsSite.addSettingsFragment("password-settings", PasswordSettingsFragment);
});

class SynchronizedSettingsFragment extends LocalStorageSettingsFragment {
    onFirstStart() {
        let res = super.onFirstStart();
        let settings = this.findBy(".setting", true);
        for (let i = 0, n = settings.length; i < n; i++) {
            settings[i].addEventListener("change", function () {
                let isCheckable = (this instanceof HTMLInputElement && (this.type === 'checkbox' || this.type === 'radio'));
                let value = this.value;
                let isChecked = isCheckable && this.checked;
                let name = this.name;

                UserManager.addIsLoggedInListener(function (isLoggedIn) {
                    if (isLoggedIn) {
                        let val = {
                            "value": JSON.stringify(null),
                            "lastUpdated": new Date().getTime()
                        };
                        if (isCheckable && isChecked || !isCheckable) {
                            val["value"] = JSON.stringify(value);
                        }
                        let res = {};
                        res[name] = val;

                        DataManager.send("u/syncSettings", {
                            "settings": JSON.stringify(res)
                        });
                    }
                });
            });
        }
        return res;
    }
}

class UserSettingsFragment extends UserFragment{
    constructor(site)
    {
        super(site, "userManagement/html/fragments/userSettings.html", "online");
    }

    onFirstStart() {
        let res = super.onFirstStart();
        (new Form(document.getElementById("user-settings-form"), "u/userSettings/set", "post")).load('u/userSettings').onSubmit(function(res){
            for (let i = 0, n = res.length; i < n; i++)
            {
                FlashMessenger.addMessage(res[i]);
            }
        });
        return res;
    }
}
InitPromise.addPromise(function(){
    SettingsSite.addSettingsFragment("user-settings", UserSettingsFragment);
});

class EditUserRolesSite extends UserSite {
    constructor(siteManager) {
        super(siteManager, 'userManagement/html/editUserRoles.html', "userRoles", "admin");
    }

    onConstruct(args) {
        let res = super.onConstruct(args);
        this.userId = args["id"];

        let self = this;
        return Promise.all([
            ScriptLoader.loadScript(Constants.SCRIPTS.LIST_JS),
            DataManager.load("u/userRoles" + DataManager.buildQuery({"id": self.userId})).then(function (res) {
                if (!res["success"]) {
                    FlashMessenger.addMessage(res["errors"][0]);
                    self.finish();
                }
                else {
                    self.setUserRoles(res["result"]["userRoles"]);
                    self.setAvailableRoles(res["result"]["availableRoles"]);
                    self.setUsername(res["result"]["username"]);
                }
            })
        ]).then(function () {
            return res;
        });
    }

    onFirstStart() {
        this.findBy("#username").innerHTML = this.username;

        const userRolesElement = this.findBy("#userRoles");
        const availableRolesElement = this.findBy("#availableRoles");

        const imgColumnUserRoles = new ImgConstSmartColumn("img/minus.png", "", false);
        const imgColumnAvailableRoles = new ImgConstSmartColumn("img/plus.png", "", false);

        const userRolesColumns = [
            new DataSmartColumn("id"),
            new SmartColumn("name", "name"),
            new SmartColumn("description", "description"),
            imgColumnUserRoles,
        ];

        const availableRolesColumns = [
            new DataSmartColumn("id"),
            new SmartColumn("name", "name"),
            new SmartColumn("description", "description"),
            imgColumnAvailableRoles,
        ];

        const userRolesListHelper = new ListHelper(userRolesElement, userRolesColumns, this.userRoles);
        const availableRolesListHelper = new ListHelper(availableRolesElement, availableRolesColumns, this.availableRoles);

        const userRolesTable = userRolesListHelper.createTable();
        const availableRolesTable = availableRolesListHelper.createTable();

        let self = this;
        const changeRoleFunction = function (roleId, addRole) {
            userRolesListHelper.setBusy(true);
            availableRolesListHelper.setBusy(true);

            return DataManager.send("u/changeUserRole", {
                "id": roleId,
                "userId": self.userId,
                "add": addRole
            }).then(function (res) {
                userRolesListHelper.setBusy(false);
                availableRolesListHelper.setBusy(false);

                if (!res["success"]) {
                    FlashMessenger.addMessage(res["errors"][0]);
                    return res;
                }

                let removingTable = null;
                let addingTable = null;
                if (res["result"]["hasRole"]) {
                    removingTable = availableRolesTable;
                    addingTable = userRolesTable;
                }
                else {
                    addingTable = availableRolesTable;
                    removingTable = userRolesTable;
                }

                const rowData = removingTable.get("id", roleId);
                if (rowData.length === 1) {
                    addingTable.add(rowData[0].values());
                    removingTable.remove("id", roleId);
                }

                return res;
            });
        };

        imgColumnUserRoles.setClickListener(function (cell) {
            let userRoleId = cell.closest("tr")["dataset"]["id"];
            changeRoleFunction(userRoleId, false);
        });

        imgColumnAvailableRoles.setClickListener(function (cell) {
            let availableRoleId = cell.closest("tr")["dataset"]["id"];
            changeRoleFunction(availableRoleId, true);
        });

    }

    setUserRoles(userRoles) {
        this.userRoles = userRoles;
    }

    setAvailableRoles(availableRoles) {
        this.availableRoles = availableRoles;
    }

    setUsername(username) {
        this.username = username;
    }
}

InitPromise.addPromise(function (app) {
    app.addDeepLink("userRoles", EditUserRolesSite);
    app.addDefaultAction(new UserAction('userRoles', function(){
        app.startSite(EditUserRolesSite);
    }, null, 1100, "admin"));
});

class ForgotPasswordSite extends UserSite{

    constructor(siteManager) {
        super(siteManager, 'userManagement/html/forgotPassword.html', "forgotPassword", "offline");
    }

    onFirstStart() {
        let self = this;
        (new Form(document.getElementById("forgot-password-form"), "u/newPassword", "post")).onSubmit(function(res){
            // UserManager.setData(res);
            // self.startStartsite();
            FlashMessenger.addMessage(FlashMessenger.MESSAGE_TYPE_SUCCESS, Translator.translate("new-password-code-send"));
            self.finish();
        });
    }
}
InitPromise.addPromise(function(app){
    app.addDeepLink("forgotPassword", ForgotPasswordSite);
});

class LoginSite extends UserSite {
    constructor(siteManager) {
        super(siteManager, 'userManagement/html/login.html', "login", "offline");
    }

    onFirstStart() {
        let self = this;
        (new LoginForm(document.getElementById("login-form"), "u/login", "post")).onSubmit(function (res) {
            UserManager.setData(res);
            self.startStartsite();
            FlashMessenger.addMessage(FlashMessenger.MESSAGE_TYPE_SUCCESS, Translator.translate("login-success"));
            self.finish();
        });

        this.findBy("#forgot-password-link").addEventListener("click", function () {
            self.startSite(ForgotPasswordSite);
            self.finish();
        });
    }
}

LoginSite.loginAction = null;
LoginSite.logoutAction = null;
LoginSite.addLoginAction = true;
LoginSite.addLogoutAction = true;

InitPromise.addPromise(function (app) {
    app.addDeepLink("login", LoginSite);

    if (Helper.isNull(LoginSite.loginAction)) {
        LoginSite.loginAction = new UserAction('login', function () {
            app.startSite(LoginSite);
        }, Menu.SHOW_NEVER, 1100, "offline");
    }
    if (Helper.isNull(LoginSite.logoutAction)) {
        LoginSite.logoutAction = new UserAction('logout', function () {
            UserManager.logOut();
        }, Menu.SHOW_NEVER, 1100, "online");
    }
    if (LoginSite.addLoginAction){
        app.addDefaultAction(LoginSite.loginAction);
    }
    if (LoginSite.addLogoutAction){
        app.addDefaultAction(LoginSite.logoutAction);
    }
});

class RegistrationSite extends UserSite {
    constructor(siteManager) {
        super(siteManager, 'userManagement/html/registration.html', "registration", "offline");
    }

    onFirstStart() {
        (new RegistrationForm(document.getElementById("registration-form"), "u/registration", "post")).onSubmit(function (res) {
            FlashMessenger.addMessage(FlashMessenger.MESSAGE_TYPE_SUCCESS, Translator.translate("registration-success"));
        });
    }
}

RegistrationSite.action = null;
RegistrationSite.addAction = true;

InitPromise.addPromise(function (app) {
    app.addDeepLink("registration", RegistrationSite);

    if (Helper.isNull(RegistrationSite.action)) {
        RegistrationSite.action = new UserAction('registration', function () {
            app.startSite(RegistrationSite);
        }, null, 1100, "offline");
    }
    if (RegistrationSite.addAction) {
        app.addDefaultAction(RegistrationSite.action);
    }
});

class SetNewPasswordSite extends UserSite {
    constructor(siteManager) {
        super(siteManager, 'userManagement/html/setNewPassword.html', "newPassword", "offline");
    }

    onConstruct(args) {
        this.code = args["code"];
        return super.onConstruct(args);
    }

    onFirstStart() {
        let formElem = document.getElementById("new-password-form");
        document.getElementById("code").value = this.code;

        let self = this;
        (new Form(formElem, "c/code", "post")).onSubmit(function(res){
            FlashMessenger.addMessage(FlashMessenger.MESSAGE_TYPE_SUCCESS, Translator.translate("password-updated"));
            self.startSite(LoginSite);
            self.finish();
        });
    }
}
InitPromise.addPromise(function(app){
    app.addDeepLink("newPassword", SetNewPasswordSite);
});

export { UserAction, UserManager, UserSite, LoginForm, RegistrationForm, PasswordSettingsFragment, SynchronizedSettingsFragment, UserFragment, UserSettingsFragment, EditUserRolesSite, ForgotPasswordSite, LoginSite, NotAllowedSite, RegistrationSite, SetNewPasswordSite };
