import { Flex, Box, Group, Typography, BoxProps } from '@tidbcloud/uikit'

import { Tag, type TagGroupItem } from 'dbaas/components'
import { RBACRoleUINames } from 'dbaas/contexts/access-control/interface/rbac'
import { CentralConsoleUser } from 'dbaas/services/account/interface'
import { useTidbServiceListTidbs } from 'dbaas/services/api-clients/v2025/tidb-management/v1beta2'
import useStores from 'dbaas/stores/useStores'

export const RoleScopeTypes = {
  ORG: 'org',
  PROJECT: 'project',
  TIDB: 'tidb'
} as const

export type ScopeType = (typeof RoleScopeTypes)[keyof typeof RoleScopeTypes]

interface ScopeInfo {
  scopeType: string
  scopeId: string
  scopeName: string
}

interface AggregatedRole {
  rbacRole: string
  scopes: ScopeInfo[]
}

export const RolesView = ({
  user,
  projectOptions,
  instanceOptions
}: {
  user: Pick<CentralConsoleUser, 'org_role' | 'project_roles' | 'cluster_roles'>
  projectOptions: { label: string; value: string }[]
  instanceOptions: { label: string; value: string }[]
}) => {
  const roleMap = new Map<string, AggregatedRole>()
  const projectOptionsMap = projectOptions.reduce(
    (acc, option) => {
      acc[option.value] = option.label
      return acc
    },
    {} as Record<string, string>
  )
  const instanceOptionsMap = instanceOptions.reduce(
    (acc, option) => {
      acc[option.value] = option.label
      return acc
    },
    {} as Record<string, string>
  )

  // Process project roles
  user.project_roles?.forEach((role) => {
    if (role.rbac_role) {
      const key = role.rbac_role
      const scopeInfo = {
        scopeType: role.scope_type || RoleScopeTypes.PROJECT,
        scopeId: role.scope_id || '',
        scopeName: projectOptionsMap[role.scope_id || ''] || ''
      }

      const existing = roleMap.get(key)
      if (existing) {
        existing.scopes.push(scopeInfo)
      } else {
        roleMap.set(key, {
          rbacRole: role.rbac_role,
          scopes: [scopeInfo]
        })
      }
    }
  })

  // Process cluster roles
  user.cluster_roles?.forEach((role) => {
    if (role.rbac_role) {
      const key = role.rbac_role
      const scopeInfo = {
        scopeType: role.scope_type || RoleScopeTypes.TIDB,
        scopeId: role.scope_id || '',
        scopeName: instanceOptionsMap[role.scope_id || ''] || role.scope_id || ''
      }

      const existing = roleMap.get(key)
      if (existing) {
        existing.scopes.push(scopeInfo)
      } else {
        roleMap.set(key, {
          rbacRole: role.rbac_role,
          scopes: [scopeInfo]
        })
      }
    }
  })

  const aggregatedRoles = Array.from(roleMap.values())
  const maxVisible = 2

  let OrgRoleView: React.ReactNode = null
  if (user.org_role?.rbac_role) {
    const orgRoleName = RBACRoleUINames[user.org_role.rbac_role] || user.org_role.rbac_role
    if (orgRoleName) {
      OrgRoleView = <Tag label={orgRoleName} size="sm" disabled />
    }
  }

  const roleItems = aggregatedRoles.reduce<TagGroupItem[]>((acc, aggregatedRole, index) => {
    const displayName = getRoleDisplayName(aggregatedRole)
    if (!displayName) {
      return acc
    }

    acc.push({
      id: `${aggregatedRole.rbacRole}-${index}`,
      label: displayName,
      tagProps: {
        size: 'sm'
      },
      children:
        aggregatedRole.scopes.length > 0
          ? aggregatedRole.scopes.map((scope, scopeIndex) => ({
              id: `${scope.scopeType}-${scope.scopeId}-${scopeIndex}`,
              label: `${scope.scopeName || scope.scopeId};`,
              tagProps: {
                size: 'sm',
                disabled: true
              }
            }))
          : undefined
    })

    return acc
  }, [])

  return (
    <Group gap={8} wrap="wrap">
      {OrgRoleView}
      <Tag.Group items={roleItems} maxVisible={maxVisible} />
    </Group>
  )
}

const getRoleDisplayName = (aggregatedRole: AggregatedRole): string => {
  const roleName = RBACRoleUINames[aggregatedRole.rbacRole] || aggregatedRole.rbacRole
  if (!roleName) {
    return ''
  }

  return aggregatedRole.scopes.length > 1 ? `${roleName} (${aggregatedRole.scopes.length})` : roleName
}

export const RolesDetail = ({
  rolesMaps,
  title,
  roles,
  ...rest
}: {
  title: string
  rolesMaps: Record<string, string[]>
  roles: { label: string; value: string }[]
} & BoxProps) => {
  return (
    <Box
      mt={16}
      sx={(theme) => ({
        border: `1px solid ${theme.colors.carbon[4]}`,
        borderRadius: theme.defaultRadius
      })}
      {...rest}
    >
      <Flex
        justify="space-between"
        align="center"
        h={40}
        px={16}
        sx={(theme) => ({
          backgroundColor: theme.colors.carbon[2],
          borderTopRightRadius: theme.defaultRadius,
          borderTopLeftRadius: theme.defaultRadius
        })}
      >
        <Typography variant="body-lg" fw={500} c="carbon.8">
          {title}
        </Typography>
      </Flex>
      <Flex px={16} py={12} direction="column">
        {roles.map((role) => {
          const roleItems = rolesMaps[role.label]
          if (!Array.isArray(roleItems) || roleItems.length === 0) {
            return null
          }
          return (
            <Flex
              align="flex-start"
              key={role.value}
              gap={8}
              sx={(theme) => ({
                '&:not(:last-child)': {
                  borderBottom: `1px solid ${theme.colors.carbon[4]}`,
                  paddingBottom: 12,
                  marginBottom: 12
                }
              })}
            >
              <Box w={224} sx={{ flexShrink: 0 }}>
                <Tag label={role.label} size="sm" disabled />
              </Box>
              <Typography variant="body-md" c="carbon.8">
                {roleItems.filter(Boolean).join('; ')}
              </Typography>
            </Flex>
          )
        })}
      </Flex>
    </Box>
  )
}

export const useRolesGroup = (roles?: Pick<CentralConsoleUser, 'project_roles' | 'cluster_roles' | 'org_role'>) => {
  const {
    store: {
      userProfile,
      lastVisitLocation,
      tenantData: { name: orgName }
    }
  } = useStores()
  const projectNames = userProfile.projects.reduce(
    (acc, project) => {
      acc[project.id] = project.name
      return acc
    },
    {} as Record<string, string>
  )

  const { data: tidbs } = useTidbServiceListTidbs(
    {
      orgId: lastVisitLocation.orgId,
      projectId: lastVisitLocation.projectId,
      tidbIds: roles?.cluster_roles?.map((role) => role.scope_id || ''),
      pageSize: 100,
      skip: 0
    },
    {
      query: {
        enabled: !!roles?.cluster_roles?.length,
        refetchOnWindowFocus: false
      }
    }
  )

  const instanceNames = tidbs?.tidbs?.reduce(
    (acc, tidb) => {
      acc[tidb.tidbId || ''] = tidb.displayName || ''
      return acc
    },
    {} as Record<string, string>
  )

  const projectRoles = roles?.project_roles?.reduce(
    (acc, role) => {
      const roleName = RBACRoleUINames[role.rbac_role || '']
      if (!roleName) {
        return acc
      }
      const scopeName = projectNames[role.scope_id || ''] || role.scope_id || ''
      if (acc[roleName]) {
        acc[roleName].push(scopeName)
      } else {
        acc[roleName] = [scopeName]
      }
      return acc
    },
    {} as Record<string, string[]>
  )

  const instanceRoles = roles?.cluster_roles?.reduce(
    (acc, role) => {
      const roleName = RBACRoleUINames[role.rbac_role || '']
      if (!roleName) {
        return acc
      }
      const scopeName = instanceNames?.[role.scope_id || ''] || role.scope_id || ''
      if (acc[roleName]) {
        acc[roleName].push(scopeName)
      } else {
        acc[roleName] = [scopeName]
      }
      return acc
    },
    {} as Record<string, string[]>
  )

  const orgRoleName = RBACRoleUINames[roles?.org_role?.rbac_role || '']

  return {
    projectRoles,
    instanceRoles,
    orgRole: orgRoleName ? { [orgRoleName]: [orgName] } : {}
  }
}
