import {
  Button,
  Form,
  Input,
  InputNumber,
  Select,
  Table,
  FormInstance,
  Popconfirm,
  TreeSelect,
  Modal,
  Checkbox
} from 'antd'
import { ColumnsType } from 'antd/lib/table'
import { DataNode } from 'antd/lib/tree'
import dayjs, { Dayjs } from 'dayjs'
import { capitalize, startCase } from 'lodash'
import React, { Fragment, useMemo, useState } from 'react'
import { Link } from 'react-router-dom'

import { useGetCreditTypeTree } from 'apiClient/api'
import { FormsCreditTypeNode } from 'apiClient/api/interface'
import { useGetCustomersByPartnerId } from 'apiClient/services/account'
import {
  createCredits,
  reclaimCredits,
  useListTenantsCredits
} from 'apiClient/services/billing'
import {
  FormsBillingConfig,
  FormsCreateCreditsPackageReq,
  FormsCreditsPackage
} from 'apiClient/services/billing/interface'
import DatePicker from 'components/DatePicker'
import ModalForm from 'components/ModalForm'
import { ListToolBar } from 'components/Table'
import { toFixed } from 'utils/number'
import { format } from 'utils/time'

import { isStartupProgramTargetType } from '../shared/startupProgram'

import { CloudProviderSelect } from './CloudProvider'

enum CreditsPackageStatus {
  Active = 'active',
  InActive = 'inactive',
  BeforeActive = 'before_active'
}

export enum CreditsPackageUsage {
  Prepayment = 'prepayment',
  POC = 'poc',
  Redeem = 'redeem',
  Program = 'program',
  StartupProgram = 'startup_program',
  Others = 'others',
  CustomerSuccess = 'customer_success'
}

type FormsCreateCreditsPackageReqData = Omit<
  FormsCreateCreditsPackageReq,
  'start_date' | 'expired_date'
> & {
  start_date?: Dayjs
  expired_date?: Dayjs
}

const statusMap: Record<string, string> = {
  [CreditsPackageStatus.BeforeActive]: 'valid',
  [CreditsPackageStatus.Active]: 'valid'
}
const fmt = 'YYYY-MM-DDTHH:mm:ss[Z]'
const EMPTY_CREDIT_TYPE_TREE: FormsCreditTypeNode[] = []

type CreditTypeTreeDataNode = DataNode & {
  value: string | number
}

const collectStartupProgramAllowedTypeIds = (
  tree: FormsCreditTypeNode[],
  isAllowedSubtree = false
): Set<string> => {
  return tree.reduce((allowedTypeIds, node) => {
    const typeId = node.type_id?.toString() || ''
    const isAllowed = isAllowedSubtree || isStartupProgramTargetType(node)

    if (isAllowed && typeId) {
      allowedTypeIds.add(typeId)
    }

    if (node.children?.length) {
      collectStartupProgramAllowedTypeIds(node.children, isAllowed).forEach(
        (allowedTypeId) => {
          allowedTypeIds.add(allowedTypeId)
        }
      )
    }

    return allowedTypeIds
  }, new Set<string>())
}

const collectStartupProgramDefaultTypeIds = (
  tree: FormsCreditTypeNode[]
): Array<string | number> => {
  return tree.reduce((defaultTypeIds, node) => {
    if (isStartupProgramTargetType(node) && node.type_id) {
      defaultTypeIds.push(node.type_id)
    }

    if (node.children?.length) {
      defaultTypeIds.push(...collectStartupProgramDefaultTypeIds(node.children))
    }

    return defaultTypeIds
  }, [] as Array<string | number>)
}

const getTreeData = (
  tree: FormsCreditTypeNode[],
  allowedTypeIds?: Set<string>
): CreditTypeTreeDataNode[] => {
  return tree.map((t) => {
    const children = t.children ? getTreeData(t.children, allowedTypeIds) : []
    const typeId = t.type_id?.toString() || ''
    const isAllowedType = !allowedTypeIds || allowedTypeIds.has(typeId)
    const hasAllowedDescendant = children.some((child) => !child.disabled)

    const data: CreditTypeTreeDataNode = {
      title: t.name,
      value: t.type_id || '',
      key: t.type_id || '',
      children
    }

    if (allowedTypeIds && !isAllowedType) {
      data.disableCheckbox = true
      data.selectable = false
      data.disabled = !hasAllowedDescendant
    }

    return data
  })
}

const AddCreditForm: React.FC<
  React.PropsWithChildren<{
    orgId: string
    isPartner?: boolean
    billingConfigId: number
    onAdd: () => void
  }>
> = ({ orgId, billingConfigId, onAdd, isPartner }) => {
  const [visible, setVisible] = useState(false)
  const [usage, setUsage] = useState<CreditsPackageUsage | undefined>(undefined)
  const { data } = useGetCreditTypeTree()
  const creditTypeTree = data?.data?.children || EMPTY_CREDIT_TYPE_TREE
  const startupProgramAllowedTypeIds = useMemo(() => {
    return collectStartupProgramAllowedTypeIds(creditTypeTree)
  }, [creditTypeTree])
  const startupProgramDefaultTypeIds = useMemo(() => {
    return collectStartupProgramDefaultTypeIds(creditTypeTree)
  }, [creditTypeTree])
  const creditTypeTreeData = useMemo(() => {
    return getTreeData(
      creditTypeTree,
      usage === CreditsPackageUsage.StartupProgram
        ? startupProgramAllowedTypeIds
        : undefined
    )
  }, [creditTypeTree, startupProgramAllowedTypeIds, usage])

  const onSubmit = async (data: FormsCreateCreditsPackageReqData) => {
    if (data.bind_types) {
      const bindTypes = (data.bind_types as Array<string | number>).filter(
        (typeId) => {
          if (data.usage !== CreditsPackageUsage.StartupProgram) {
            return true
          }

          return startupProgramAllowedTypeIds.has(typeId.toString())
        }
      )

      // @ts-ignore
      data.bind_types = bindTypes.map((it) => {
        return {
          type_id: it
        }
      })
    }
    await createCredits(
      orgId,
      billingConfigId,
      {
        ...data,
        start_date: data.start_date?.format(fmt),
        expired_date: data.expired_date?.format(fmt)
      },
      { timeout: 15 * 1000 }
    )
    onAdd()
    setVisible(false)
  }

  const defaultTime = dayjs(dayjs().format('YYYY-MM-DD') + ' 00:00:00')

  const timeFormat = (value: Dayjs) => {
    return format(value.format(fmt), undefined, 'YYYY-MM-DD HH:mm:ss Z', 0)
  }

  const { data: customers } = useGetCustomersByPartnerId(
    orgId,
    {
      page: 1,
      per_page: 1000
    },
    {
      query: {
        enabled: isPartner,
        refetchInterval: false,
        refetchOnWindowFocus: false
      }
    }
  )

  const renderFields = (form: FormInstance) => {
    return (
      <>
        {isPartner && (
          <Form.Item name="customers" label="End Customer Org">
            <Select mode="multiple">
              {(customers?.data || []).map((item) => {
                return (
                  <Select.Option
                    key={item.customer_org_id || ''}
                    value={item.customer_org_id || ''}
                  >
                    {item.name} (Org ID: {item.customer_org_id})
                  </Select.Option>
                )
              })}
            </Select>
          </Form.Item>
        )}
        <Form.Item
          name="usage"
          label="Usage"
          required
          rules={[{ required: true, message: 'Required' }]}
        >
          <Select
            onChange={(value) => {
              setUsage(value as CreditsPackageUsage)
              if (value === CreditsPackageUsage.POC) {
                form.setFieldsValue({
                  bind_types: data?.data
                    ? [data.data.children?.[0]?.type_id]
                    : []
                })
              } else if (value === CreditsPackageUsage.StartupProgram) {
                form.setFieldsValue({
                  bind_types: startupProgramDefaultTypeIds
                })
              } else {
                form.setFieldsValue({
                  bind_types: []
                })
              }
            }}
          >
            {[
              CreditsPackageUsage.Prepayment,
              CreditsPackageUsage.POC,
              CreditsPackageUsage.Redeem,
              CreditsPackageUsage.Program,
              CreditsPackageUsage.StartupProgram,
              CreditsPackageUsage.CustomerSuccess,
              CreditsPackageUsage.Others
            ].map((t) => (
              <Select.Option key={t} value={t}>
                {startCase(t.replace('_', ' '))}
              </Select.Option>
            ))}
          </Select>
        </Form.Item>
        <Form.Item
          name="bind_types"
          label="Credit Type"
          required
          rules={[{ required: true, message: 'Required' }]}
        >
          <TreeSelect
            showCheckedStrategy="SHOW_PARENT"
            treeCheckable
            treeDefaultExpandAll
            treeData={creditTypeTreeData}
          />
        </Form.Item>
        <CloudProviderSelect />
        {usage === CreditsPackageUsage.Program && (
          <Form.Item
            name="usage_extend"
            label="Program Info"
            rules={[
              { max: 128, message: 'Should be less than 128 characters' }
            ]}
          >
            <Input></Input>
          </Form.Item>
        )}
        <Form.Item
          name="discount_enabled"
          label="Apply credits after discount"
          valuePropName="checked"
        >
          <Checkbox />
        </Form.Item>
        <Form.Item
          name="total_credits"
          label="Credits"
          required
          rules={[{ required: true, message: 'Required' }]}
        >
          <InputNumber<string>
            stringMode
            min="0"
            step="0.01"
            precision={2}
            style={{ display: 'inline-block', width: '100%' }}
          />
        </Form.Item>
        <Form.Item
          name="start_date"
          label="Start Date"
          required
          rules={[{ required: true, message: 'Required' }]}
          extra="Current time zone is UTC±00:00"
        >
          <DatePicker
            showNow={false}
            showTime={{
              defaultValue: defaultTime,
              format: 'HH:mm:ss'
            }}
            format={timeFormat}
          />
        </Form.Item>
        <Form.Item
          name="expired_date"
          label="Expiry Date"
          required
          rules={[
            ({ getFieldValue }) => ({
              validator(rule, value: Dayjs) {
                if (!value) {
                  return Promise.reject(new Error('Required'))
                }
                const startDate = getFieldValue('start_date') as Dayjs
                if (!startDate) {
                  return Promise.reject('Please fill Start Date')
                }
                if (value.isBefore(startDate)) {
                  return Promise.reject(
                    new Error('Expiry Date should be greater than Start Date')
                  )
                }
                return Promise.resolve()
              }
            })
          ]}
        >
          <DatePicker
            showNow={false}
            showTime={{
              defaultValue: defaultTime,
              format: 'HH:mm:ss'
            }}
            format={timeFormat}
          />
        </Form.Item>
        <Form.Item
          name="note"
          label="Note"
          rules={[{ max: 128, message: 'Should be less than 128 characters' }]}
        >
          <Input.TextArea style={{ height: 80 }}></Input.TextArea>
        </Form.Item>
      </>
    )
  }

  return (
    <div>
      <Button type="primary" onClick={() => setVisible(true)}>
        Add Credit
      </Button>
      <ModalForm<FormsCreateCreditsPackageReqData>
        width={700}
        layout={{
          labelCol: { span: 8 }
        }}
        initValues={{
          discount_enabled: true
        }}
        name="add-credit-form"
        // labelAlign="left"
        title="Add Credit"
        visible={visible}
        onSubmit={onSubmit}
        onCancel={() => setVisible(false)}
        getFields={renderFields}
      ></ModalForm>
    </div>
  )
}

export const ManageCredits: React.FC<
  React.PropsWithChildren<{
    billingConfig: FormsBillingConfig
    isPartner?: boolean
  }>
> = ({ billingConfig, isPartner }) => {
  const orgId = billingConfig.tenant_id || ''
  const { data, refetch, isFetching } = useListTenantsCredits(
    orgId,
    billingConfig.billing_config_id,
    {
      query: {
        refetchInterval: 60 * 1000
      }
    }
  )

  const onReclaim = (creditsPackage: FormsCreditsPackage) => async () => {
    setReclaiming(creditsPackage.credits_package_id || null)
    try {
      await reclaimCredits(
        creditsPackage.tenant_id?.toString() || '',
        billingConfig.billing_config_id,
        creditsPackage.credits_package_id?.toString() || ''
      )
      refetch()
    } finally {
      setReclaiming(null)
    }
  }

  const [reclaiming, setReclaiming] = useState<number | null>(null)

  const columns: ColumnsType<FormsCreditsPackage> = [
    {
      title: 'Credit ID',
      dataIndex: 'credits_package_id'
    },
    {
      title: 'Credit Code',
      dataIndex: 'credits_code'
    },
    {
      title: 'Status',
      render: (_: any, record: FormsCreditsPackage) => {
        return (
          record.closed_reason ||
          statusMap[record.status || ''] ||
          record.status ||
          ''
        )
          .split('_')
          .map(capitalize)
          .join(' ')
      }
    },
    {
      title: 'Cloud Provider',
      render: (_: any, record) => {
        return record.provider?.toUpperCase() || '-'
      }
    },
    {
      title: 'Credit Type',
      render: (_: any, record: FormsCreditsPackage) => {
        const { type, bind_types } = record
        if (!type) {
          return '-'
        }
        if (bind_types && bind_types.length <= 2) {
          return type
        }
        return (
          <span>
            {bind_types
              ?.slice(0, 2)
              .map((t) => {
                return t.name
              })
              .join(', ')}
            <a
              onClick={() => {
                Modal.confirm({
                  title: 'Credit Type',
                  content: type,
                  cancelButtonProps: {
                    style: { display: 'none' }
                  },
                  okText: 'Close'
                })
              }}
              style={{ marginLeft: '4px' }}
            >
              Details
            </a>
          </span>
        )
      }
    },
    {
      title: 'Total Credits',
      render: (_: any, record: FormsCreditsPackage) => {
        return toFixed(record.total_credits || 0)
      }
    },
    {
      title: 'Remaining Credits',
      render: (_: any, record: FormsCreditsPackage) => {
        return toFixed(record.remaining_credits || 0)
      }
    },
    {
      title: 'Apply credits after discount',
      render: (_: any, record: FormsCreditsPackage) => {
        return record.discount_enabled ? 'Yes' : 'No'
      }
    },
    {
      title: 'Credit Start Date',
      render: (_: any, record: FormsCreditsPackage) => {
        return `${format(
          record.start_date,
          undefined,
          'YYYY-MM-DD HH:mm:ss Z',
          0
        )}`
      }
    },
    {
      title: 'Credit Expiry Date',
      render: (_: any, record: FormsCreditsPackage) => {
        return `${format(
          record.expired_date,
          undefined,
          'YYYY-MM-DD HH:mm:ss Z',
          0
        )}`
      }
    },
    {
      title: 'Usage',
      dataIndex: 'usage',
      render(_: any, record, index) {
        const usage = startCase((record.usage || '').replace('_', ' '))
        if (record.usage_extend) {
          return `${usage}:${record.usage_extend}`
        }
        return usage
      }
    },
    {
      title: 'Note',
      dataIndex: 'note'
    },
    {
      title: 'Action',
      width: 180,
      render: (_: any, record: FormsCreditsPackage) => {
        if (record.status === CreditsPackageStatus.InActive) {
          return null
        }
        return (
          <Popconfirm
            title={
              <>
                <b>Reclaim Credit</b>
                <p>Are you sure to reclaim this credit?</p>
              </>
            }
            placement="right"
            okText="Confirm"
            cancelText="Cancel"
            onConfirm={onReclaim(record)}
          >
            <Button
              type="primary"
              loading={reclaiming === record.credits_package_id}
            >
              Reclaim
            </Button>
          </Popconfirm>
        )
      }
    }
  ]
  if (isPartner) {
    columns.splice(1, 0, {
      title: 'Customers',
      dataIndex: 'customers',
      render: (_: any, record) => {
        return record.customers?.map((it, index) => {
          return (
            <Fragment key={it}>
              <Link to={`/orgs/${it}`}>{it}</Link>
              {index < (record.customers?.length || 0) - 1 && <br />}
            </Fragment>
          )
        })
      }
    })
  }

  return (
    <div>
      <ListToolBar
        actions={[
          <AddCreditForm
            orgId={orgId}
            onAdd={refetch}
            isPartner={isPartner}
            billingConfigId={billingConfig.billing_config_id}
          />
        ]}
        reload={async () => {
          await refetch()
        }}
      ></ListToolBar>
      <Table
        loading={isFetching}
        columns={columns}
        dataSource={data?.data || []}
        rowKey="credits_package_id"
      ></Table>
    </div>
  )
}
