import { Injectable } from "@angular/core";
import { ActivatedRoute, Params } from "@angular/router";
import { AllowType, MenuType } from "../enums";
import { ApiResources } from "../helpers";
import { IMenuModel, IMenuOverAllModel } from "../models";
import { HttpService } from "./http.service";
import { NotifyService } from "./notify.service";

@Injectable()
export class MenuService {
    private records: Array<IMenuModel>;
    private access: boolean;
    isHitMenu = true;
    private menuButtonCodes: Array<string>;

    constructor(
        private readonly notifyService: NotifyService,
        private readonly httpService: HttpService,
    ) { }

    private setFromLocal = () => {
        if (!this.records) {
            const localMenus = localStorage.getItem("menus");
            if (localMenus) {
                const data = (JSON.parse(localMenus) as IMenuOverAllModel);
                this.records = data.menus;
                this.access = data.isFullAccess;
                this.menuButtonCodes = data.menuButtonCodes;
                this.isHitMenu = true;
            }
        }
    }

    private compare = (a: IMenuModel, b: IMenuModel) => {
        if (a.priority < b.priority) {
            return -1;
        }
        if (a.priority > b.priority) {
            return 1;
        }
        return 0;
    }

    fetch = (accountId: number, roleId: number, callback?: Function) => {
        this.httpService
            .post<IMenuOverAllModel>(ApiResources.getURI(ApiResources.account.base,
                ApiResources.account.getMenus),
                { accountId: accountId, roleId: roleId }, false)
            .subscribe((data: IMenuOverAllModel) => {
                this.setMenus = data;
                this.isHitMenu = false;
                callback();
            });
    }

    getFirstSubMenuByUrl = (url: string) => {
        const lastIndex = url.lastIndexOf("/");
        url = url.substring(0, lastIndex);

        const record = this.records.find(x => x.url.toLowerCase() === url.toLowerCase());
        if (record) {
            const subMenu = this.records.filter(x =>
                x.menuTypeId === MenuType.SubMenu &&
                x.mainPage === record.mainPage &&
                x.url.indexOf(":") === -1);
            if (subMenu.length > 0) {
                return subMenu[0].url;
            }
        }

        return null;
    }



    menus = (menuType: MenuType = null, mainPage: string = null, subPage: string = null): Array<IMenuModel> => {
        this.setFromLocal();
        if (!this.records) return new Array<IMenuModel>();

        let filtered = this.records;
        if (menuType) {
            filtered = filtered.filter(x => MenuType.SubMenu === menuType
                ? x.menuTypeId === menuType || x.menuTypeId === MenuType.CategoryMenu
                : x.menuTypeId === menuType);
        }

        if (mainPage) {
            filtered = filtered.filter(x => x.mainPage === mainPage);
        }

        if (subPage) {
            filtered = filtered.filter(x => x.subPage === subPage);
        }

        const final = filtered.sort(this.compare);

        final.forEach(item => {
            const urlTokens = item.url.split("/");
            for (let i = urlTokens.length - 1; i >= 0; i--) {
                if (urlTokens[i].indexOf(":") === -1) {
                    item.subPage = urlTokens[i];
                    break;
                }
            }
        });

        return final;
    }

    set setMenus(data: IMenuOverAllModel) {
        this.records = data.menus;
        this.access = data.isFullAccess;
        this.menuButtonCodes = data.menuButtonCodes;
        localStorage.setItem("menus", JSON.stringify(data));
    }

    get getDefaultRoute(): string {
        this.setFromLocal();
        return this.records && this.records.length > 0 ? this.records[0].url : "login";
    }

    removeFromLocal = () => {
        localStorage.removeItem("menus");
    }

    isMenuButtonAllowed = (code: string) => {
        if(!this.menuButtonCodes) {
            this.setFromLocal();
        }
        return this.access || (this.menuButtonCodes || [] as Array<string>).find(x => x === code);
    }

    isAllowed = (uri: string, showNotify = false): AllowType | string => {
        this.setFromLocal();

        if (!this.records) return AllowType.BreakIt;

        if (this.access) return AllowType.Allowed;

        const currentSplit = uri.split("/");
        let isOverallPass = false;
        const matchedUrls = this.records.filter(x => x.count === currentSplit.length);
        for (let i = 0; i < matchedUrls.length; i++) {
            const url = matchedUrls[i].url;
            let isPass = false;
            const existingSplit = url.split("/");
            for (let j = 0; j < existingSplit.length; j++) {
                const item = existingSplit[j];
                if (item.indexOf(":") !== -1) {
                    isPass = true;
                    continue;
                }

                if (j >= currentSplit.length) {
                    isPass = false;
                    break;
                }

                if (item === currentSplit[j]) {
                    isPass = true;
                } else {
                    isPass = false;
                    break;
                }
            }

            if (isPass) {
                isOverallPass = true;
                break;
            }
        }

        if (showNotify && !isOverallPass) {
            this.notifyService.hideAll();

            const isAlternative = this.getFirstSubMenuByUrl(uri);
            if (isAlternative) {
                return isAlternative;
            } else {
                this.notifyService.info(`You do not have access to this page (${uri}).`);
            }
        }

        return isOverallPass ? AllowType.Allowed : AllowType.NotAllowed;
    }

    getNextRoute = (url: string, params: Params, route: ActivatedRoute): Promise<string> => {
        return new Promise(async (resolve) => {
            let foundUrl = decodeURIComponent(decodeURIComponent(url));
            const parentParams = route.parent.params["value"] || {};
            const childParams = (params.params ? params.params : params) || {};
            const allParams = { ...parentParams, ...childParams };
            Object.keys(allParams).forEach((key) => {
                const decodedValue = decodeURIComponent(decodeURIComponent(allParams[key]));
                foundUrl = foundUrl.replace(`/${decodedValue}/`, `/:${key}/`);
            })
            const currentRoute = this.records.find((x) => x.url === foundUrl);
            if (currentRoute) {
                let nextRoute = this.records.find((x) => {
                    const basicCondition = this.basicCondition(x, currentRoute);
                    return currentRoute.menuTypeId === MenuType.CategoryMenu
                        ? basicCondition && x.category === currentRoute.category
                        : basicCondition;
                });
                if (nextRoute) {
                    resolve(this.getReverseStrategyLink(nextRoute.url, allParams));
                }

                if (currentRoute.menuTypeId === MenuType.CategoryMenu) {
                    nextRoute = this.records.find((x) => this.basicCondition(x, currentRoute));
                    if (nextRoute) {
                        resolve(this.getReverseStrategyLink(nextRoute.url, allParams));
                    }
                }

                nextRoute = this.records.find((x) => x.mainPage === currentRoute.mainPage && x.menuTypeId === MenuType.SubMenu && x.priority === 1);
                if (nextRoute) {
                    resolve(this.getReverseStrategyLink(nextRoute.url, allParams));
                }
            }
            return resolve(null);
        })
    }

    getCurrentRoute = (url: string, params: Params, route: ActivatedRoute): Promise<IMenuModel> => {
        return new Promise(async (resolve) => {
            let foundUrl = decodeURIComponent(decodeURIComponent(url));
            const parentParams = route.parent.params["value"] || {};
            const childParams = (params.params ? params.params : params) || {};
            const allParams = { ...parentParams, ...childParams };
            Object.keys(allParams).forEach((key) => {
                const decodedValue = decodeURIComponent(decodeURIComponent(allParams[key]));
                foundUrl = foundUrl.replace(`/${decodedValue}`, `/:${key}`);
            })
            const currentRoute = this.records.find((x) => x.url === foundUrl);
            return resolve(currentRoute);
        })
    }

    private getReverseStrategyLink = (url: string, params: Params) => {
        let foundUrl = url;
        const obj = params;
        Object.keys(obj).forEach((key) => {
            const encodedValue = encodeURIComponent(decodeURIComponent(decodeURIComponent(obj[key])));
            foundUrl = foundUrl.replace(`:${key}`, encodedValue);
        })
        return foundUrl;
    }

    private basicCondition = (compateTo: IMenuModel, compareWith: IMenuModel) => {
        if (compareWith) {
            return compateTo.mainPage === compareWith.mainPage && compateTo.priority === (compareWith.priority + 1);
        }

        return false;
    }
}
