import { ReloadOutlined } from '@ant-design/icons'
import { Button, Space, Table, Typography, Input, notification } from 'antd'
import { ColumnsType } from 'antd/lib/table'
import Title from 'antd/lib/typography/Title'
import { Fragment, useCallback, useMemo, useState, useEffect } from 'react'

import {
  iAMServiceResolveEntityPermissions,
  iAMServiceResolveRolePermissions
} from 'apiClient/services/core-server'
import {
  V1GrantSource,
  V1GrantedPermissions,
  V1Permission,
  V1RoleBindingTarget
} from 'apiClient/services/core-server/interface'
import CollapseCell from 'components/CollapseCell'
import {
  getRoleBindingTargetLabel,
  getRoleBindingTargetLink
} from 'pages/IAM/utils'
import { getErrorMessage } from 'utils/error'

type GrantPathItem = {
  key: string
  label: string
  link?: string
}

const GRANT_PATH_COLLAPSE_MIN_COUNT = 2

const grantPathContainerStyle = {
  display: 'flex',
  flexWrap: 'wrap',
  alignItems: 'center',
  gap: 4,
  whiteSpace: 'normal'
} as const

const grantPathNodeStyle = {
  overflowWrap: 'anywhere'
} as const

function mapGrantSourceToItem(
  source: V1GrantSource,
  index: number
): GrantPathItem {
  if (source.policy?.name) {
    const policyName = source.policy.name
    return {
      key: `policy-${policyName}-${index}`,
      label: `policy/${policyName}`,
      link: `/iam/role/policies/${policyName}`
    }
  }

  if (source.role?.name) {
    const roleName = source.role.name
    return {
      key: `role-${roleName}-${index}`,
      label: `role/${roleName}`,
      link: `/iam/role/roles/${roleName}`
    }
  }

  if (source.entity?.type && source.entity?.name) {
    const entityName = source.entity.name
    const targetLabel = getRoleBindingTargetLabel(source.entity.type)
    const targetLink = getRoleBindingTargetLink(source.entity)

    return {
      key: `entity-${source.entity.type}-${entityName}-${index}`,
      label: `${targetLabel}/${entityName}`,
      link: targetLink
    }
  }

  return {
    key: `unknown-${index}`,
    label: 'unknown/-'
  }
}

function GrantPathCell({ grantPath }: { grantPath?: V1GrantSource[] }) {
  const [expanded, setExpanded] = useState(false)

  const sourceItems = useMemo(
    () =>
      (grantPath || []).map((source, index) =>
        mapGrantSourceToItem(source, index)
      ),
    [grantPath]
  )

  useEffect(() => {
    setExpanded(false)
  }, [grantPath])

  if (!sourceItems.length) {
    return '-'
  }

  const isExpandable = sourceItems.length > GRANT_PATH_COLLAPSE_MIN_COUNT
  const visibleItems =
    expanded || !isExpandable
      ? sourceItems
      : sourceItems.slice(0, GRANT_PATH_COLLAPSE_MIN_COUNT)

  return (
    <div style={grantPathContainerStyle}>
      {visibleItems.map((sourceItem, index) => (
        <Fragment key={sourceItem.key}>
          {index > 0 && <Typography.Text>{'->'}</Typography.Text>}
          {sourceItem.link ? (
            <a href={sourceItem.link} style={grantPathNodeStyle}>
              {sourceItem.label}
            </a>
          ) : (
            <Typography.Text style={grantPathNodeStyle}>
              {sourceItem.label}
            </Typography.Text>
          )}
        </Fragment>
      ))}
      {!expanded && isExpandable && (
        <>
          <Typography.Text>{'->'}</Typography.Text>
          <Typography.Text style={grantPathNodeStyle}>...</Typography.Text>
          <Typography.Link
            onClick={() => {
              setExpanded(true)
            }}
          >
            more
          </Typography.Link>
        </>
      )}
      {expanded && isExpandable && (
        <Typography.Link
          onClick={() => {
            setExpanded(false)
          }}
        >
          less
        </Typography.Link>
      )}
    </div>
  )
}

function NestedTable({ privileges }: { privileges: V1Permission[] }) {
  const renderItem = useCallback(({ resource, operations }: V1Permission) => {
    return (
      <Space>
        <Typography>{resource}</Typography>
        <Typography>:</Typography>
        <Typography>{operations?.join('|')}</Typography>
      </Space>
    )
  }, [])

  const memoList = useMemo(() => privileges, [privileges])

  return (
    <CollapseCell<V1Permission>
      list={memoList}
      renderItem={renderItem}
      minCount={1}
    />
  )
}

export default function ShowPermissions({
  target,
  roleName
}: {
  target?: V1RoleBindingTarget
  roleName?: string
}) {
  const [curPermissions, setCurPermissions] = useState<V1GrantedPermissions[]>()
  const [seachPermissionInput, setSearchPermissionInput] = useState('')
  const [reload, setReload] = useState(false)
  const [loading, setLoading] = useState(false)

  useEffect(() => {
    fetch()
  }, [reload])

  async function fetch() {
    setLoading(true)
    try {
      if (target) {
        const permData = (await iAMServiceResolveEntityPermissions({ target }))
          .data
        setCurPermissions(permData.permissions)
      }
      if (roleName) {
        const permData = (await iAMServiceResolveRolePermissions(roleName)).data
        setCurPermissions(permData.permissions)
      }
    } catch (e) {
      notification.error({
        message: getErrorMessage(e)
      })
    } finally {
      setLoading(false)
    }
  }

  const renderPermissionCell = useCallback((record: V1GrantedPermissions) => {
    const permissions = record.permissions || []
    return <NestedTable privileges={permissions} />
  }, [])

  const columns: ColumnsType<V1GrantedPermissions> = [
    {
      title: 'Permissions',
      render: renderPermissionCell
    },
    {
      title: 'Sources',
      render: (_: any, record) => {
        return <GrantPathCell grantPath={record.grantPath} />
      }
    }
  ]

  return (
    <div>
      <Title level={4}>Permissions</Title>
      <div style={{ display: 'flex', gap: '10px', justifyContent: 'flex-end' }}>
        <div style={{ width: '40%' }}>
          <Input.Search
            placeholder="Resource to search"
            onChange={(e) => {
              setSearchPermissionInput(e.target.value)
            }}
          />
        </div>
        <div>
          <Button
            icon={<ReloadOutlined />}
            loading={loading}
            onClick={() => setReload((pre) => !pre)}
          ></Button>
        </div>
      </div>
      <br />
      <Table<V1GrantedPermissions>
        columns={columns}
        dataSource={curPermissions?.filter((v) => {
          let filted =
            v.permissions?.filter((p: V1Permission) => {
              return p.resource?.toLowerCase().includes(seachPermissionInput)
            }) || []
          return filted.length > 0
        })}
        pagination={{ pageSize: 10 }}
        size="small"
        loading={loading}
      />
    </div>
  )
}
