import { MinusOutlined, PlusOutlined, ReloadOutlined } from '@ant-design/icons'
import {
  Button,
  Card,
  Descriptions,
  Divider,
  Form,
  Input,
  Modal,
  Space,
  Table
} from 'antd'
import type { TableProps } from 'antd'
import { ColumnsType } from 'antd/lib/table'
import Title from 'antd/lib/typography/Title'
import React, { useCallback, useEffect, useState } from 'react'
import { useHistory, useParams } from 'react-router-dom'

import {
  iAMServiceBatchCreatePolicyBindings,
  iAMServiceBatchDeletePolicyBindings,
  iAMServiceDeletePolicy,
  iAMServiceGetPolicy,
  iAMServiceListPolicyBindings,
  iAMServiceUpdatePolicy
} from 'apiClient/services/core-server'
import {
  IAMServiceUpdatePolicyBody,
  V1Permission,
  V1Policy,
  V1PolicyBinding
} from 'apiClient/services/core-server/interface'
import ModalForm from 'components/ModalForm'
import { RoleSearchInput } from 'components/ResourceSearchInput/DevOps/RoleSearchInput'
import { getPolicyTypeLabel, showErrorModal } from 'pages/IAM/utils'
import { toLocal } from 'utils/time'

import PolicyModalForm from '../Policy/Forms/PolicyForm'

import DeletePolicyForm, {
  DeletePolicyFormType
} from './Forms/DeletePolicyForm'

type TableRowSelection<T extends object = object> =
  TableProps<T>['rowSelection']

export default function PolicyDetail() {
  const { policy_name } = useParams<{
    policy_name: string
  }>()
  const [curPolicyInfo, setCurPolicyInfo] = useState<V1Policy>()
  const [reload, setReload] = useState(false)
  const [editPolicy, setEditPolicy] = useState(false)
  const [deletePolicy, setDeletePolicy] = useState(false)
  const [seachPermissionInput, setSearchPermissionInput] = useState('')
  const history = useHistory()

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

  async function fetch() {
    try {
      const dataPolicy = (await iAMServiceGetPolicy(policy_name)).data
      setCurPolicyInfo(dataPolicy.policy)
    } catch (e) {
      console.error(e)
    }
  }

  const columnsPermission: ColumnsType<V1Permission> = [
    {
      title: 'Resource',
      render: (_: any, record) => {
        return record.resource
      }
    },
    {
      title: 'Operations',
      render: (_: any, record) => {
        return record.operations?.join(', ')
      }
    },
    {
      title: 'Description',
      render: (_: any, record) => {
        return record.description
      }
    }
  ]

  const handleEdit = useCallback(
    async (payload: IAMServiceUpdatePolicyBody & { name?: string }) => {
      const req = {
        description: payload.description,
        permissions: payload.permissions,
        updatePermissions: true
      }
      try {
        await iAMServiceUpdatePolicy(policy_name, req)
        Modal.success({
          title: 'Info',
          content: (
            <div>
              <p>Policy has been updated</p>
            </div>
          ),
          onOk() {}
        })
        setEditPolicy(false)
        setReload((pre) => !pre)
      } catch (error) {
        showErrorModal(error)
      }
    },
    [reload]
  )

  const renderEditFormFields = useCallback(() => {
    return <PolicyModalForm isEdit={true} />
  }, [editPolicy])

  const handleDelete = useCallback(
    async (payload: DeletePolicyFormType) => {
      try {
        await iAMServiceDeletePolicy(policy_name)
        Modal.success({
          title: 'Info',
          content: (
            <div>
              <p>{`Policy '${curPolicyInfo?.name}' has been deleted.`}</p>
            </div>
          ),
          onOk() {
            history.push('/iam/role')
          }
        })
        setDeletePolicy(false)
        setReload((pre) => !pre)
      } catch (error) {
        showErrorModal(error)
      }
    },
    [reload]
  )

  const renderDeleteFormFields = useCallback(() => {
    return <DeletePolicyForm policyName={policy_name} />
  }, [deletePolicy])

  return (
    <>
      <div>
        <Card
          title={<Title level={4}>{`Policy '${curPolicyInfo?.name}'`}</Title>}
          extra={
            <Space>
              <Button
                type="primary"
                onClick={() => {
                  setEditPolicy(true)
                }}
              >
                Edit
              </Button>
              &nbsp;
              <Button
                type="ghost"
                onClick={() => {
                  setDeletePolicy(true)
                }}
              >
                Delete
              </Button>
            </Space>
          }
        >
          <Descriptions>
            <Descriptions.Item label="ID">
              {curPolicyInfo?.id}
            </Descriptions.Item>
            <Descriptions.Item label="Name">
              {curPolicyInfo?.name}
            </Descriptions.Item>
            <Descriptions.Item label="Type">
              {getPolicyTypeLabel(curPolicyInfo?.type)}
            </Descriptions.Item>
            <Descriptions.Item label="Created At">
              {toLocal(curPolicyInfo?.createdAt)}
            </Descriptions.Item>
            <Descriptions.Item label="Updated At">
              {toLocal(curPolicyInfo?.updatedAt)}
            </Descriptions.Item>
            <Descriptions.Item label="Description">
              {curPolicyInfo?.description}
            </Descriptions.Item>
          </Descriptions>

          <Divider />

          <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>
          <br></br>
          <Table<V1Permission>
            rowKey="id"
            columns={columnsPermission}
            dataSource={curPolicyInfo?.permissions?.filter((v) => {
              return v.resource?.toLowerCase().includes(seachPermissionInput)
            })}
            pagination={{ pageSize: 10 }}
            size="small"
          ></Table>

          <Divider />
          <ShowAttachedRoles policyName={policy_name} />
        </Card>

        <ModalForm<IAMServiceUpdatePolicyBody & { name?: string }>
          visible={!!editPolicy}
          name="updatePolicy"
          title={`Update a Policy`}
          initValues={curPolicyInfo}
          getFields={renderEditFormFields}
          onSubmit={handleEdit}
          onCancel={() => {
            setEditPolicy(false)
          }}
          width={800}
        />
        <ModalForm<DeletePolicyFormType>
          visible={!!deletePolicy}
          name="deletePolicy"
          title={`Delete Policy ${curPolicyInfo?.name}`}
          getFields={renderDeleteFormFields}
          onSubmit={handleDelete}
          onCancel={() => {
            setDeletePolicy(false)
          }}
          width={600}
        />
      </div>
    </>
  )
}

type CreatePolicyBindingsFormValue = {
  roleNames: string[]
}

function CreatePolicyBindingsForm() {
  return (
    <Form.Item label="Roles" name="roleNames" rules={[{ required: true }]}>
      <RoleSearchInput
        mode="multiple"
        placeholder="Search by role name"
        style={{ width: '100%' }}
      />
    </Form.Item>
  )
}

function ShowAttachedRoles({ policyName }: { policyName: string }) {
  const [bindings, setBindings] = useState<V1PolicyBinding[]>()
  const [searchValue, setSearchValue] = useState<string>('')
  const [createPolicyBindings, setCreatePolicyBindings] = useState(false)
  const [selectedRowKeys, setSelectedRowKeys] = useState<React.Key[]>([])
  const [loading, setLoading] = useState(false)
  const [reload, setReload] = useState(false)

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

  async function fetch() {
    setLoading(true)
    try {
      const data = (
        await iAMServiceListPolicyBindings({
          policyIs: policyName,
          page: 0
        })
      ).data
      setBindings(data.policyBindings)
    } catch (e) {
      console.error(e)
    } finally {
      setLoading(false)
    }
  }

  const handleCreatePolicyBindings = useCallback(
    async (payload: CreatePolicyBindingsFormValue) => {
      try {
        await iAMServiceBatchCreatePolicyBindings({
          policyNames: [policyName],
          roleNames: payload.roleNames
        })
        Modal.success({
          title: 'Info',
          content: (
            <div>
              <p>Policy bindings have been created.</p>
            </div>
          ),
          onOk() {}
        })
        setCreatePolicyBindings(false)
        setReload((pre) => !pre)
      } catch (error) {
        showErrorModal(error)
      }
    },
    [policyName]
  )

  const handleDeletePolicyBindings = useCallback(() => {
    Modal.info({
      title: 'Info',
      content: (
        <div>
          <p>Are you sure to delete selected policy bindings?</p>
          <p>{selectedRowKeys.join(', ')}</p>
        </div>
      ),
      async onOk() {
        try {
          await iAMServiceBatchDeletePolicyBindings({
            ids: selectedRowKeys.map((key) => key.toString())
          })
          Modal.success({
            title: 'Info',
            content: (
              <div>
                <p>Policy bindings have been deleted.</p>
              </div>
            ),
            onOk() {}
          })
          setSelectedRowKeys([])
          setReload((pre) => !pre)
        } catch (error) {
          showErrorModal(error)
        }
      }
    })
  }, [selectedRowKeys])

  const renderCreatePolicyBindingsFormFields = useCallback(() => {
    return <CreatePolicyBindingsForm />
  }, [createPolicyBindings])

  const onSelectChange = (newSelectedRowKeys: React.Key[]) => {
    setSelectedRowKeys(newSelectedRowKeys)
  }

  const rowSelection: TableRowSelection<V1PolicyBinding> = {
    selectedRowKeys,
    onChange: onSelectChange
  }

  const columns: ColumnsType<V1PolicyBinding> = [
    {
      title: 'Name',
      render: (_: any, record) => {
        return (
          <a href={'/iam/role/roles/' + record.roleName}>{record.roleName}</a>
        )
      }
    },
    {
      title: 'Created At',
      render: (_: any, record) => {
        return toLocal(record.createdAt)
      }
    }
  ]

  return (
    <div>
      <Title level={4}>Attached Roles</Title>
      <div style={{ display: 'flex', gap: '10px', justifyContent: 'flex-end' }}>
        <div style={{ width: '40%' }}>
          <Input.Search
            onChange={(e) => {
              setSearchValue(e.target.value)
            }}
          />
        </div>
        <div>
          <Button
            icon={<PlusOutlined />}
            type="primary"
            onClick={() => {
              setCreatePolicyBindings(true)
            }}
          >
            Create
          </Button>
        </div>
        <div>
          <Button
            icon={<MinusOutlined />}
            type="primary"
            disabled={selectedRowKeys.length === 0}
            onClick={handleDeletePolicyBindings}
          >
            Delete
          </Button>
        </div>
        <div>
          <Button
            icon={<ReloadOutlined />}
            onClick={() => {
              setSelectedRowKeys([])
              setReload((pre) => !pre)
            }}
          ></Button>
        </div>
      </div>
      <br></br>

      <Table
        columns={columns}
        dataSource={bindings?.filter((v) => {
          return v.roleName?.toLowerCase().includes(searchValue.toLowerCase())
        })}
        rowKey={'id'}
        rowSelection={rowSelection}
        loading={loading}
        pagination={{ pageSize: 10 }}
      />

      <ModalForm<CreatePolicyBindingsFormValue>
        visible={!!createPolicyBindings}
        name="createPolicyBindings"
        title={`Create Policy Bindings for Policy '${policyName}'`}
        initValues={{
          roleNames: []
        }}
        getFields={renderCreatePolicyBindingsFormFields}
        onSubmit={handleCreatePolicyBindings}
        onCancel={() => {
          setCreatePolicyBindings(false)
        }}
        width={800}
      />
    </div>
  )
}
