import { Role, User } from '@things-factory/auth-base'
import { Domain } from '@things-factory/shell'
import { Menu } from '@things-factory/menu-base'
import { getRepository, SelectQueryBuilder, In } from 'typeorm'

export const opaMenusResolver = {
  async opaMenus(_: any, _params: any, context: any) {
    let menus: Menu[] = []
    if (context?.state?.domain?.id && context?.state?.user?.id) {
      try {
        const qb: SelectQueryBuilder<User> = getRepository(User).createQueryBuilder('USER')
        const user: User = await qb
          .leftJoinAndSelect('USER.roles', 'ROLES')
          .leftJoinAndSelect('ROLES.domain', 'ROLES_DOMAIN')
          .where('USER.id = :userId', { userId: context.state.user.id })
          .andWhere('ROLES_DOMAIN.id = :domainId', { domainId: context.state.domain.id })
          .getOne()

        // get menus which are non-restricted by any role.
        menus.push(...(await getNonRestrictedMenus(context.state.domain)))

        const userRoleIds: string[] = user.roles.map((role: Role) => role.id)
        let restrictedMenus: Menu[] = []
        if (userRoleIds?.length) {
          // get menus which are restricted by role ()
          restrictedMenus = [...(await getRestrictedMenus(userRoleIds, context.state.domain))]
        }

        const restrictedMenuIds: string[] = restrictedMenus.map((restrictedMenu: Menu) => restrictedMenu.id)
        menus.forEach(menu => {
          const idx = restrictedMenuIds.indexOf(menu.id)
          if (idx !== -1) {
            menu.children = Array.from(new Set([...restrictedMenus[idx].children, ...menu.children]))
          }
        })

        return menus.filter((groupMenu: Menu) => groupMenu?.children?.length)
      } catch (e) {
        throw e
      }
    }
  }
}

async function getNonRestrictedMenus(domain: Domain): Promise<Menu[]> {
  const menus: Menu[] = await getRepository(Menu).find({
    where: { domain, hiddenFlag: false, menuType: 'MENU' },
    relations: ['parent', 'children', 'children.roles'],
    order: { rank: 'ASC' }
  })

  menus.forEach(menu => {
    menu.children = menu.children.filter(child => !child.hiddenFlag && !child.roles?.length)
  })

  return menus
}

async function getRestrictedMenus(roleIds: string[], domain: Domain): Promise<Menu[]> {
  const menus: Menu[] = await getRepository(Menu).find({
    where: { domain, hiddenFlag: false, menuType: 'MENU' },
    relations: ['parent', 'children', 'children.roles'],
    order: { rank: 'ASC' }
  })
  const userRoles: Role[] = await getRepository(Role).find({ where: { domain, id: In(roleIds) } })

  menus.forEach(menu => {
    menu.children = menu.children.filter(child => {
      if (child.hiddenFlag) {
        return false; // Exclude child if hiddenFlag is true
      }
      const childRoleIds = child.roles?.map(role => role.id) || []

      return userRoles.find(role => childRoleIds.includes(role.id)) || false
    })
  })

  return menus
}
