import { ReloadOutlined } from '@ant-design/icons'
import {
  Badge,
  Button,
  Card,
  Descriptions,
  Modal,
  Table,
  Space,
  Divider
} from 'antd'
import { ColumnsType } from 'antd/lib/table'
import Title from 'antd/lib/typography/Title'
import moment from 'moment'
import { useCallback, useEffect, useState } from 'react'
import { Link, useParams } from 'react-router-dom'

import {
  operationControllerCancelInstance,
  operationControllerUpdateInstance,
  operationControllerFinishInstanceAction,
  operationControllerGetInstance,
  operationControllerStartInstanceAction,
  operationControllerSubmitInstance,
  operationControllerGetOperation
} from 'apiClient/services/devops'
import {
  AuditControllerListAuditLogType,
  OperationActionStatus,
  OperationInstance,
  OperationInstanceAction,
  OperationInstancePhase,
  OperationActionType,
  OperationActionStartPolicy,
  OperationUpdateInstanceReq,
  OperationOperation,
  OperationOperationTarget
} from 'apiClient/services/devops/interface'
import ApprovalTable from 'components/ApprovalTable'
import AuditTable from 'components/Audit'
import ModalForm from 'components/ModalForm'
import { ClusterDescription } from 'components/ResourceDescription/ClusterDescription'
import { ResourcePoolDescription } from 'components/ResourceDescription/ResourcePoolDescription'
import { isSharedResourcePoolTenantId } from 'components/ResourceDescription/shared'
import { TenantDescription } from 'components/ResourceDescription/TenantDescription'
import { toLocal } from 'pages/Devops/Maintenance/Detail'
import DisplayOpsExecutionWindow from 'pages/Devops/Maintenance/DisplayOpsExecutionWindow'
import { redactJsonDisplayValue } from 'utils/jsonRedaction'

import FinishInstanceActionForm, {
  FinishInstanceActionFormType
} from './Forms/FinishInstanceActionForm'
import UpdateInstanceForm, {
  UpdateInstanceFormType
} from './Forms/UpdateInstanceForm'
import {
  SortByWeight,
  InstancePhaseDisplay,
  ActionStatusDisplay
} from './utils'

export default function OperationDetail() {
  const { instance_id } = useParams<{ instance_id: string }>()

  const [curInstance, setCurInstance] = useState<OperationInstance>()
  const [curOperation, setCurOperation] = useState<OperationOperation>()

  const [showUpdateForm, setShowUpdateForm] = useState(false)
  const [finishForm, setFinishForm] = useState(false)
  const [finishID, setFinishID] = useState<number>()

  const [reload, setReload] = useState(false)

  async function fetch() {
    const ins = (await operationControllerGetInstance(parseInt(instance_id)))
      .data.instance
    setCurInstance(ins)

    const op = (await operationControllerGetOperation(ins?.operation!)).data
      .operation
    setCurOperation(op)
  }

  const renderStatusCell = useCallback(
    (_: any, record: OperationInstanceAction) => {
      return <ActionStatusDisplay status={record.status} />
    },
    []
  )

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

  const handleClickFinish = (id: number | undefined) => {
    return () => {
      setFinishID(id)
      setFinishForm(true)
    }
  }

  const handleSubmit = async () => {
    Modal.info({
      title: 'Info',
      closable: true,
      content: (
        <div>
          <p>Confirm to submit this draft operation?</p>
        </div>
      ),
      okText: 'Confirm',
      async onOk() {
        try {
          await operationControllerSubmitInstance(parseInt(instance_id))
          Modal.success({
            title: 'Info',
            content: (
              <div>
                <p>Submit the draft operation successfully</p>
              </div>
            ),
            onOk() {}
          })
          setReload((pre) => !pre)
        } catch (error) {
          Modal.error({
            title: error.response.data.errcode,
            content: error.response.data.errmsg
          })
        }
      }
    })
  }

  const buildUpdateInitialValues = (
    instance: OperationInstance,
    operation: OperationOperation
  ): UpdateInstanceFormType => {
    const params: { [key: string]: any } = {}
    if (instance.params) {
      for (const key of Object.keys(
        SortByWeight(instance.params, operation.params)
      )) {
        params[key] = instance.params?.[key]
      }
    }

    return {
      actions:
        instance.actions?.map((action) => ({
          name: action.name!,
          start_policy: action.start_policy!,
          schedule_time: action.start_schedule_at
            ? moment.unix(action.start_schedule_at)
            : undefined
        })) || [],
      reason: instance.reason,
      params: params
    }
  }

  const renderUpdateForm = useCallback(() => {
    return (
      <UpdateInstanceForm
        cur_instance={curInstance || {}}
        cur_operation={curOperation || {}}
      />
    )
  }, [showUpdateForm])

  const renderFormFields = useCallback(() => {
    return <FinishInstanceActionForm />
  }, [finishForm])

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

  const handleUpdate = async (payload: UpdateInstanceFormType) => {
    try {
      const req: OperationUpdateInstanceReq = {
        params: payload.params,
        reason: payload.reason,
        actions: payload.actions?.map((action) => ({
          name: action.name,
          start_policy: action.start_policy,
          start_schedule_at: action.schedule_time?.unix()
        }))
      }
      await operationControllerUpdateInstance(parseInt(instance_id), req)
      Modal.success({
        title: 'info',
        content: (
          <div>
            <p>Successfully Update</p>
          </div>
        ),
        onOk() {}
      })
      setShowUpdateForm(false)
      setReload((pre) => !pre)
    } catch (error) {
      Modal.error({
        title: error.response.data.errcode,
        content: error.response.data.errmsg
      })
      setReload((pre) => !pre)
    }
  }

  const handleFinish = async (payload: FinishInstanceActionFormType) => {
    try {
      if (finishID != undefined) {
        await operationControllerFinishInstanceAction(
          parseInt(instance_id),
          finishID,
          {
            result:
              OperationActionStatus[
                payload.result as keyof typeof OperationActionStatus
              ],
            output: payload.output
          }
        )
        Modal.success({
          title: 'info',
          content: (
            <div>
              <p>Successfully Finish</p>
            </div>
          ),
          onOk() {}
        })
        setFinishForm(false)
        setReload((pre) => !pre)
      }
    } catch (error) {
      Modal.error({
        title: error.response.data.errcode,
        content: error.response.data.errmsg
      })
      setReload((pre) => !pre)
    }
  }

  const handleCancelFinish = useCallback(() => {
    setFinishForm(false)
  }, [])

  const handleClickStart = (order: number, i: number) => {
    return async () => {
      let window_inf
      if (
        curInstance?.actions &&
        curInstance.actions[i].start_policy ===
          OperationActionStartPolicy.manual
      ) {
        window_inf = 'sure to start this action?'
      } else {
        window_inf = `The start policy is "${
          curInstance?.actions
            ? curInstance?.actions[i].start_policy
            : undefined
        }", and the action will be started automatically, did you confirm to start it now?`
      }
      Modal.info({
        title: 'Info',
        closable: true,
        content: (
          <div>
            <p>{window_inf}</p>
          </div>
        ),
        okText: 'Confirm',
        async onOk() {
          try {
            await operationControllerStartInstanceAction(
              parseInt(instance_id),
              order
            )
            Modal.success({
              title: 'info',
              content: (
                <div>
                  <p>Successfully start the action</p>
                </div>
              ),
              onOk() {}
            })
            setReload((pre) => !pre)
          } catch (error) {
            Modal.error({
              title: error.response.data.errcode,
              content: error.response.data.errmsg
            })
            setReload((pre) => !pre)
          }
        }
      })
    }
  }

  const handleClickCancel = async () => {
    Modal.info({
      title: 'Info',
      closable: true,
      content: (
        <div>
          <p>Sure to cancel this action?</p>
        </div>
      ),
      okText: 'Confirm',
      async onOk() {
        try {
          await operationControllerCancelInstance(parseInt(instance_id))
          Modal.success({
            title: 'info',
            content: (
              <div>
                <p>Successfully Cancel</p>
              </div>
            ),
            onOk() {}
          })
          setReload((pre) => !pre)
        } catch (error) {
          Modal.error({
            title: error.response.data.errcode,
            content: error.response.data.errmsg
          })
          setReload((pre) => !pre)
        }
      }
    })
  }

  const enableStart = (i: number) => {
    if (
      curInstance?.phase === OperationInstancePhase.pending ||
      curInstance?.phase === OperationInstancePhase.running
    ) {
      if (i === 0) {
        return (
          curInstance?.actions &&
          curInstance.actions[i].status === OperationActionStatus.created
        )
      } else {
        return (
          curInstance?.actions &&
          (curInstance.actions[i - 1].status ===
            OperationActionStatus.succeeded ||
            curInstance.actions[i - 1].status ===
              OperationActionStatus.skipped) &&
          curInstance.actions[i].status === OperationActionStatus.created
        )
      }
    } else {
      return false
    }
  }

  const enableFinish = (i: number) => {
    return (
      curInstance?.actions &&
      curInstance.actions[i].type === OperationActionType.manual &&
      curInstance.actions[i].status === OperationActionStatus.running
    )
  }

  const enableCancel = () => {
    return (
      curInstance?.phase === OperationInstancePhase.draft ||
      curInstance?.phase === OperationInstancePhase.created ||
      curInstance?.phase === OperationInstancePhase.approving ||
      curInstance?.phase === OperationInstancePhase.pending ||
      curInstance?.phase === OperationInstancePhase.running
    )
  }

  const columnsParams: ColumnsType<{ name: string; value: unknown }> = [
    {
      title: 'Param Name',
      render: (_: any, record) => {
        return record.name
      }
    },
    {
      title: 'Param Value',
      onCell: () => {
        return {
          style: {
            whiteSpace: 'pre-wrap'
          }
        }
      },
      render: (_: any, record) => {
        const { isJson, displayText } = redactJsonDisplayValue(record.value)

        if (
          !isJson &&
          typeof record.value === 'string' &&
          record.name.includes('link')
        ) {
          return (
            <a href={record.value} target="_blank" rel="noreferrer">
              {record.value}
            </a>
          )
        }

        return displayText
      }
    }
  ]

  const columnsOutputs: ColumnsType<{ name: string; value: unknown }> = [
    {
      title: 'Output Name',
      render: (_: any, record) => {
        return record.name
      }
    },
    {
      title: 'Output Value',
      onCell: () => {
        return {
          style: {
            whiteSpace: 'pre-wrap'
          }
        }
      },
      render: (_: any, record) => {
        const { isJson, displayText } = redactJsonDisplayValue(record.value)
        return displayText
      }
    }
  ]

  const columnsActions: ColumnsType<OperationInstanceAction> = [
    {
      title: 'Name',
      render: (_: any, record) => {
        return record.name
      }
    },
    {
      title: 'Status',
      render: renderStatusCell
    },
    {
      title: 'Start Policy',
      render: (_: any, record) => {
        return record.start_policy
      }
    },
    {
      title: 'Time',
      render: (_: any, record) => {
        return (
          <>
            {`Schedule Time: ${toLocal(record.start_schedule_at) || ''}`}
            <br />
            {`Start Time: ${toLocal(record.started_at) || ''}`}
            <br />
            {`Finish Time: ${toLocal(record.finished_at) || ''}`}
          </>
        )
      }
    },
    {
      title: 'Output',
      render: (_: any, record) => {
        return record.output
      }
    },
    {
      title: 'Operation',
      render: (_: any, record, i) => {
        return (
          <div>
            <Button
              type="primary"
              onClick={
                record.order !== undefined
                  ? handleClickStart(record.order, i)
                  : () => {}
              }
              disabled={!enableStart(i)}
            >
              Start
            </Button>
            &nbsp;
            {record.type == `auto` ? (
              <Button
                type="primary"
                onClick={
                  record.order !== undefined
                    ? handleClickStart(record.order, i)
                    : () => {}
                }
                disabled={record.status !== OperationActionStatus.failed}
              >
                Retry
              </Button>
            ) : (
              <></>
            )}
            &nbsp;
            {record.type !== `auto` ? (
              <Button
                type="primary"
                onClick={
                  record.order !== undefined
                    ? handleClickFinish(record.order)
                    : () => {}
                }
                disabled={!enableFinish(i)}
              >
                Finish
              </Button>
            ) : (
              <></>
            )}
          </div>
        )
      }
    }
  ]

  const [activeTabKey, setActiveTabKey] = useState<string>('approval')
  const onTabChange = (key: string) => {
    setActiveTabKey(key)
  }

  const tabList = [
    {
      key: 'approval',
      tab: 'Approval Info'
    },
    {
      key: 'audit',
      tab: 'Audit Logs'
    }
  ]

  const contentList: Record<string, React.ReactNode> = {
    audit: (
      <AuditTable
        type={AuditControllerListAuditLogType.operation}
        id={curInstance?.id}
      />
    ),
    approval: curInstance?.approval_id ? (
      <ApprovalTable id={curInstance?.approval_id} />
    ) : (
      <div>Loading...</div>
    )
  }

  const renderTargetDetail = () => {
    const params = curInstance?.params

    switch (curInstance?.type) {
      case OperationOperationTarget.cluster:
        return params?.cluster_id ? (
          <ClusterDescription clusterId={params.cluster_id} />
        ) : null
      case OperationOperationTarget.tenant:
        if (isSharedResourcePoolTenantId(params?.tenant_id)) {
          return params?.resource_pool_id ? (
            <ResourcePoolDescription resourcePoolId={params.resource_pool_id} />
          ) : (
            <TenantDescription tenantId={params.tenant_id} />
          )
        }

        return params?.tenant_id ? (
          <TenantDescription tenantId={params.tenant_id} />
        ) : null
      case OperationOperationTarget.resource_pool:
        return params?.resource_pool_id ? (
          <ResourcePoolDescription resourcePoolId={params.resource_pool_id} />
        ) : null
      default:
        return null
    }
  }

  const targetDetail = renderTargetDetail()

  return (
    <div>
      <Card
        title={<Title level={4}>{`Operation Detail`}</Title>}
        extra={
          <Space>
            {curInstance?.phase === OperationInstancePhase.draft && (
              <Button
                type="primary"
                disabled={curInstance?.phase !== OperationInstancePhase.draft}
                onClick={handleSubmit}
              >
                Submit
              </Button>
            )}
            {curInstance?.phase === OperationInstancePhase.draft && (
              <Button
                type="primary"
                disabled={curInstance?.phase !== OperationInstancePhase.draft}
                onClick={() => setShowUpdateForm(true)}
              >
                Update
              </Button>
            )}
            <Button
              type="primary"
              onClick={handleClickCancel}
              disabled={!enableCancel()}
            >
              Cancel
            </Button>
            &nbsp;
            <Button
              onClick={() => {
                setReload((pre) => !pre)
              }}
              icon={<ReloadOutlined />}
            ></Button>
          </Space>
        }
      >
        <Title level={5}>Basic Information</Title>
        <Descriptions>
          <Descriptions.Item label="ID">{curInstance?.id}</Descriptions.Item>
          <Descriptions.Item label="Type">
            {curInstance?.type}
          </Descriptions.Item>
          <Descriptions.Item label="Operation">
            {curInstance?.operation}
          </Descriptions.Item>
          <Descriptions.Item label="Applicant">
            {curInstance?.applicant}
          </Descriptions.Item>
          <Descriptions.Item label="Phase">
            <InstancePhaseDisplay phase={curInstance?.phase} />
          </Descriptions.Item>
          <Descriptions.Item label="Created At">
            {toLocal(curInstance?.created_at)}
          </Descriptions.Item>
          <Descriptions.Item label="Close At">
            {toLocal(curInstance?.closed_at)}
          </Descriptions.Item>
          <Descriptions.Item label="Reason">
            {curInstance?.reason}
          </Descriptions.Item>
        </Descriptions>

        {targetDetail && (
          <>
            <Divider />
            <Title level={5}>Target Detail</Title>
            {targetDetail}
          </>
        )}

        <DisplayOpsExecutionWindow
          executionWindow={curInstance?.execution_window}
          inWindowMessage="Current time is within the Exection Window. Execution is allowed."
          outOfWindowMessage="Current time is outside the Execute Window. Execution is not allowed."
        />

        <Divider />

        <Title level={5}>Parameters</Title>
        <Table<{ name: string; value: unknown }>
          columns={columnsParams}
          rowKey="id"
          dataSource={Object.keys(
            SortByWeight(curInstance?.params || {}, curOperation?.params)
          ).map((key) => ({
            name: key,
            value: curInstance?.params ? curInstance?.params[key] : ``
          }))}
          size="small"
          pagination={false}
        ></Table>

        {curInstance?.outputs ? (
          <div>
            <Divider />

            <Title level={5}>Outputs</Title>
            <Table<{ name: string; value: unknown }>
              columns={columnsOutputs}
              rowKey="id"
              dataSource={
                curInstance?.outputs
                  ? Object.keys(curInstance?.outputs).map((key) => ({
                      name: key,
                      value: curInstance?.outputs
                        ? curInstance?.outputs[key]
                        : ``
                    }))
                  : []
              }
              size="small"
              pagination={false}
            ></Table>
          </div>
        ) : (
          <></>
        )}

        <Divider />

        <Title level={5}>Actions</Title>
        <Table<OperationInstanceAction>
          columns={columnsActions}
          rowKey="id"
          dataSource={curInstance?.actions}
          size="small"
          pagination={false}
        ></Table>
      </Card>

      <Card
        tabList={tabList}
        tabProps={{ size: 'middle' }}
        bordered={false}
        activeTabKey={activeTabKey}
        onTabChange={(key) => {
          onTabChange(key)
        }}
      >
        {contentList[activeTabKey]}
      </Card>

      <ModalForm<FinishInstanceActionFormType>
        visible={!!finishForm}
        name="user"
        title={'Finish Operation'}
        getFields={renderFormFields}
        onSubmit={handleFinish}
        onCancel={handleCancelFinish}
      />

      <ModalForm<UpdateInstanceFormType>
        visible={showUpdateForm}
        name="update-instance"
        title={'Update Operation'}
        getFields={renderUpdateForm}
        width={1000}
        onSubmit={handleUpdate}
        onCancel={() => setShowUpdateForm(false)}
        initValues={buildUpdateInitialValues(
          curInstance || {},
          curOperation || {}
        )}
      />
    </div>
  )
}
