import { immutable } from '../decorators';
import { View, Element, config } from '..';
import nuimsService from '../../service/nuims';

const roleName2Permission = new Map();

interface Role{
    DomainName: string,
    RoleId: string,
    RoleName: string,
    RoleStatus: number,
}

interface RoleItem extends Role {
    permission?: Permission,
    checked?: Boolean,
    toggling?: Boolean,
}

interface Permission{
    DomainName: string,
    PermissionId: string,
    PermissionValue: string,
}
interface PermissionResource{
    Action: string,
    Description: string,
    DomainName: string,
    Label: string,
    ResourceId: string,
    ResourceType: string,
    ResourceValue: string,
}

interface Resource{
    Description?: string,
    DomainName: string,
    Label?: string,
    ParentResourceType?: string,
    ParentResourceValue?: string,
    ReferenceCount?: string,
    ResourceId?: string,
    ResourceType: string,
    ResourceValue: string,
}

export class Nuims {
    /**
     * App Name
     */
    @immutable()
    public readonly domainName: string = undefined;
    /**
     * 上报权限后的资源id
     */
    @immutable()
    public readonly resourceId: string = undefined;

    /**
     * view
     */
    @immutable()
    public readonly view: View = undefined;

    /**
     * element
     */
    @immutable()
    public readonly element: Element = undefined;

    /**
     * @param source 需要合并的部分参数
     */
    constructor(source?: Partial<Nuims>) {
        const { domainName } = source;
        Object.assign(this, source);
        if (!domainName) {
            throw new Error('Nuims need domainName!');
        }
    }

    async getInitRoles() {
        let isCreated = false;
        let resource = await this.getResource();
        if (!resource) {
            isCreated = true;
            resource = await this.createResource();
        }
        const { ResourceId, ResourceValue } = resource;
        Object.assign(this, { resourceId: ResourceId });
        const roles = await this.getRoles();
        const initRoles: RoleItem[] = await Promise.all(roles.map(async (role: Role) => {
            const roleItem: RoleItem = role;
            let permission = await this.getDefaultPermissionForRole(role);
            if (permission) {
                permission.PermissionValue = permission.PermissionValue || `${role.RoleName}-default-permission`;
            } else {
                permission = await this.createDefaultPermission(role);
                await this.bindRolesAndPermissions(role, permission);
            }
            roleName2Permission.set(role.RoleName, permission);
            // 创建 /dashboard 资源时自动关联 DEV-AdminRole 权限
            if (isCreated && (ResourceValue === '/dashboard' || ((ResourceValue === '/index' || ResourceValue === '/user') && config.scope === 'h5')) && role.RoleName === 'DEV-AdminRole') {
                await this.attachPermissionResourceAction();
            }
            roleItem.permission = permission;
            const resources = await this.getResourcesFromPermission(permission);
            roleItem.checked = !!resources.find((resource: Resource) => resource.ResourceId === ResourceId);
            roleItem.toggling = false;
            return roleItem;
        }));
        return initRoles;
    }

    /**
     * 绑定dev权限
     * 用于用户创建页面、子页面的时候
     * @returns
     */
    async attachDevRole() {
        // 获取权限树资源Id
        let resource = await this.getResource();
        if (!resource) {
            resource = await this.createResource();
        }
        Object.assign(this, { resourceId: resource.ResourceId });
        // 处理角色与默认权限
        await this.handleDefaultPermisssionForRole();
        // 绑定Dev role权限与资源
        await this.attachPermissionResourceAction();
    }

    async handleDefaultPermisssionForRole() {
        // 获取角色列表
        const roles = await this.getRoles();
        // 遍历角色列表，如果角色没有关联的默认权限，创建默认权限
        const promises = roles.map(async (role: Role) => {
            let permission = await this.getDefaultPermissionForRole(role);
            if (permission) {
                permission.PermissionValue = permission.PermissionValue || `${role.RoleName}-default-permission`;
            } else {
                permission = await this.createDefaultPermission(role);
                await this.bindRolesAndPermissions(role, permission);
            }
            roleName2Permission.set(role.RoleName, permission);
        });
        await Promise.all(promises);
    }

    /**
     * 获取当前资源
     */
    async getResource() {
        const resourceValue = this.getResourceValue();
        const resource = await this.getResourceFromResourceValue(resourceValue);
        return resource;
    }

    /**
     * 创建资源
     */
    async createResource() {
        const resourceValue = this.getResourceValue();
        await this.createResourceFromResourceValue(resourceValue);
        const resource = await this.getResourceFromResourceValue(resourceValue);
        return resource;
    }

    /**
     * 批量创建资源，创建当前 View / Element 及其子页面/元素关联的所有资源
     */
    async createResources() {
        if (this.element) {
            this.createElementResources(this.element);
        }
    }

    /**
     * 批量创建资源，创建 Element 及其子元素关联的所有资源
     */
    createElementResources(element: Element) {
        this.createResourceFromResourceValue(this.getResourceValueFromElement(element));
        const { children } = element;
        if (children.length) {
            children.forEach((child: Element) => {
                this.createElementResources(child);
            });
        }
    }

    /**
     * 删除当前 View / Element 及其子页面/元素关联的所有资源
     */
    deleteResources() {
        if (this.element) {
            this.deleteElementResources(this.element);
        } else {
            this.deleteViewResources(this.view);
        }
    }

    /**
     * 批量删除资源，删除 View 及其子页面关联的所有资源
     */
    deleteViewResources(view: View) {
        const { children, tempPath } = view;
        this.deleteResourceFromResourceValue(tempPath);
        if (children.length) {
            children.forEach((child: View) => {
                this.deleteViewResources(child);
            });
        }
    }

    /**
     * 批量删除资源，删除 Element 及其子元素关联的所有资源
     */
    deleteElementResources(element: Element) {
        this.deleteResourceFromResourceValue(this.getResourceValueFromElement(element));
        const { children } = element;
        if (children.length) {
            children.forEach((child: Element) => {
                this.deleteElementResources(child);
            });
        }
    }

    /**
     * 根据 resourceValue 获取关联资源
     */
    async getResourceFromResourceValue(resourceValue: string) {
        if (!resourceValue)
            return;

        const { Data: resources } = await nuimsService.getResources({
            query: {
                Limit: 1000,
            },
            body: {
                filter: {
                    DomainName: this.domainName,
                    ResourceValue: resourceValue,
                    ResourceType: 'ui',
                },
            },
        });
        return resources.find((item: Resource) => item.ResourceValue === resourceValue);
    }

    /**
     * 根据 resourceValue 创建关联资源
     */
    async createResourceFromResourceValue(resourceValue: string) {
        if (!resourceValue)
            return;

        await nuimsService.createResources({
            body: [{
                DomainName: this.domainName,
                ResourceValue: resourceValue,
                ResourceType: 'ui',
                Description: this.getDescription(),
            }],
        });
    }

    /**
     * 根据 resourceValue 删除关联资源
     */
    async deleteResourceFromResourceValue(resourceValue: string) {
        if (!resourceValue)
            return;

        try {
            await nuimsService.deleteResources({
                body: [{
                    DomainName: this.domainName,
                    ResourceValue: resourceValue,
                    ResourceType: 'ui',
                }],
                config: { noErrorTip: true },
            });
        } catch (error) {
            console.error(error);
        }
    }

    /**
     * 根据 resourceValue 编辑关联资源
     */
    async editResourceFromResourceValue(resourceValue: string) {
        if (!resourceValue)
            return;

        const oldResource = await this.getResourceFromResourceValue(resourceValue);
        if (!oldResource) {
            return;
        }

        const { ResourceId } = oldResource;
        Object.assign(this, { resourceId: ResourceId });
        await nuimsService.editResources({
            body: [{ ResourceId, ResourceValue: this.getResourceValue() }],
            config: { noErrorTip: true },
        })
    }

    /**
     * 根据 Permission 获取关联资源
     */
    async getResourcesFromPermission(permission: Permission) {
        const res = await nuimsService.getPermissionResources({
            query: {
                Limit: 1000,
            },
            body: {
                DomainName: this.domainName,
                PermissionValue: permission.PermissionValue,
                filter: {},
            },
        });
        return res.Data;
    }

    getResourceValueFromElement(element: Element) {
        return element.name ? `${element.view.tempPath}/${element.name}` : null;
    }

    getResourceValue() {
        return this.element ? this.getResourceValueFromElement(this.element) : this.view.tempPath;
    }

    getDescription() {
        return this.element ? this.element.view.title : this.view.title;
    }

    /**
     * 获取角色列表
     */
    async getRoles() {
        const res = await nuimsService.getRoles({
            query: {
                DomainName: this.domainName,
            },
        });
        return res.Data;
    }

    /**
     * 1.创建角色
     * 2.创建角色的默认权限
     * 3.绑定角色和权限
     */
    async createDefaultRoleFromRoleName(roleName: string) {
        if (!roleName)
            return;
        const res = await nuimsService.createRole({
            body: {
                DomainName: this.domainName,
                RoleName: roleName,
            },
        });
        const role = res.Data;
        const permission = await this.createDefaultPermission(role);
        await this.bindRolesAndPermissions(role, permission);
    }

    async getDefaultPermissionForRole(role: Role) {
        const { RoleName } = role;
        if (roleName2Permission.has(RoleName))
            return roleName2Permission.get(RoleName);

        const permissionValue = `${RoleName}-default-permission`;
        const res = await nuimsService.getPermissionsForRole({
            query: {
                Limit: 1000,
            },
            body: {
                DomainName: this.domainName,
                RoleName,
                Filter: {
                    PermissionValue: permissionValue,
                },
            },
        });
        return res.Data[0];
    }

    getPermissionResources(permission: Permission) {
        const { PermissionValue } = permission;
        return nuimsService.getPermissionResources({
            query: {
                Limit: 1000,
            },
            body: {
                DomainName: this.domainName,
                PermissionValue,
                filter: {},
            },
        }).then((res:any) => res.Data);
    }

    /**
     * 为角色创建默认权限
     */
    async createDefaultPermission(role: Role) {
        const { RoleName } = role;
        if (!RoleName)
            return;

        const { domainName } = this;
        const permissionValue = `${RoleName}-default-permission`;
        const permission = await nuimsService.getPermissions({
            body: {
                filter: {
                    DomainName: domainName,
                    PermissionValue: permissionValue,
                },
            },
        }).then((res: any) => res.Data[0]);
        if (permission)
            return permission;

        const res = await nuimsService.createPermission({
            body: {
                DomainName: domainName,
                PermissionValue: permissionValue,
            },
        });
        return Object.assign({ DomainName: domainName, PermissionValue: permissionValue }, res.Data);
    }

    /**
     * 关联角色和权限
     */
    bindRolesAndPermissions(role: Role, permission: Permission) {
        const { RoleId } = role;
        const { PermissionId } = permission;
        return nuimsService.bindRolesAndPermissions({
            body: {
                RoleIdList: [RoleId],
                PermissionIdList: [PermissionId],
            },
        });
    }

    /**
     * 绑定权限和资源
     */
    async attachPermissionResourceAction() {
        const permission = roleName2Permission.get('DEV-AdminRole');
        const { PermissionId } = permission;
        // 查找权限绑定的资源，如果该资源未绑定才需要调绑定接口
        const resources = await this.getPermissionResources(permission);
        const checked = !!resources.find((item: PermissionResource) => item.ResourceId === this.resourceId);
        if (checked) {
            return;
        }
        try {
            await this.attachPermissionResource(PermissionId);
        } catch (err) {

        }
    }
    /**
     * 解绑权限和资源
     */
    async attachPermissionResource(permissionId: string) {
        await nuimsService.attachPermissionResourceAction({
            body: {
                PermissionId: permissionId,
                ResourceIdActionList: [{ ResourceId: this.resourceId }],
            },
            config: {
                noErrorTip: true,
            },
        });
    }

    async detachPermissionResource(permissionId: string) {
        await nuimsService.detachPermissionResourceAction({
            body: {
                PermissionId: permissionId,
                ResourceIdActionList: [{ ResourceId: this.resourceId }],
            },
            config: {
                noErrorTip: true,
            },
        });
    }
}

export default Nuims;
