import { DownloadOutlined, UploadOutlined } from '@ant-design/icons'
import {
  Button,
  Input,
  message,
  Modal,
  Space,
  Table,
  Typography,
  Upload
} from 'antd'
import { ColumnsType } from 'antd/lib/table'
import { RcFile } from 'antd/lib/upload'
import Papa from 'papaparse'
import { useEffect, useMemo, useState } from 'react'

import {
  getApiV1DevopsBillingProxyApiV1BillingUnitsExport,
  useGetApiV1DevopsBillingProxyApiV1BillingUnits,
  usePatchApiV1DevopsBillingProxyApiV1BillingUnitsUnitKey,
  usePostApiV1DevopsBillingProxyApiV1BillingUnitsImport
} from 'apiClient/services/billing'
import {
  FormsSKUGroup,
  FormsUnitInfo
} from 'apiClient/services/billing/interface'
import { ListToolBar } from 'components/Table'

import type { SKUGroupModalType } from './SKUGroupModal'
import { UnitListModal } from './UnitListModal'
import { isManualPriceSKUGroup } from './utils'

export interface UnitListProps {
  skuGroup: FormsSKUGroup
  type: SKUGroupModalType
}

export const UnitListTable = ({ skuGroup, type }: UnitListProps) => {
  const isManualPrice = isManualPriceSKUGroup(skuGroup)
  const unitFilters = {
    group_id: skuGroup.group_id
  }
  const { data, refetch } = useGetApiV1DevopsBillingProxyApiV1BillingUnits(
    unitFilters,
    { query: { refetchOnMount: false } }
  )
  const [search, setSearch] = useState('')
  const filteredData = search
    ? data?.data?.filter((item) => (item.unit || '').includes(search))
    : data?.data
  const [openModal, setOpenModal] = useState(false)
  const [editingUnit, setEditingUnit] = useState<FormsUnitInfo>()
  const [isExporting, setIsExporting] = useState(false)
  const { mutateAsync: importUnits, isLoading: isImporting } =
    usePostApiV1DevopsBillingProxyApiV1BillingUnitsImport()

  const exportUnits = async () => {
    setIsExporting(true)
    try {
      const response = await getApiV1DevopsBillingProxyApiV1BillingUnitsExport(
        unitFilters
      )
      const fileUrl = URL.createObjectURL(response.data)
      const link = document.createElement('a')

      link.href = fileUrl
      link.download = `${skuGroup.name || 'units'}.csv`
      link.click()
      URL.revokeObjectURL(fileUrl)
    } catch (e: any) {
      message.error(e.response?.data?.message || 'Export failed.')
    } finally {
      setIsExporting(false)
    }
  }

  const importUnitFile = (file: RcFile) => {
    if (!skuGroup.category) {
      message.error('The current SKU Group does not have a category.')
      return Upload.LIST_IGNORE
    }
    if (skuGroup.group_id === undefined) {
      message.error('The current SKU Group does not have a group ID.')
      return Upload.LIST_IGNORE
    }

    Papa.parse<Record<string, string>>(file, {
      header: true,
      skipEmptyLines: true,
      transformHeader: (header) => header.replace(/^\uFEFF/, ''),
      complete: async ({ data: rows, errors, meta }) => {
        if (errors.length) {
          message.error(`Invalid CSV: ${errors[0].message}`)
          return
        }
        const requiredColumns = ['category', 'group_id']
        const missingColumns = requiredColumns.filter(
          (column) => !meta.fields?.includes(column)
        )
        if (missingColumns.length) {
          message.error(
            `The CSV must contain ${missingColumns.join(', ')} column${
              missingColumns.length > 1 ? 's' : ''
            }.`
          )
          return
        }

        const csv = Papa.unparse(
          rows.map((row) => ({
            ...row,
            category: skuGroup.category,
            group_id: String(skuGroup.group_id)
          })),
          { columns: meta.fields }
        )

        try {
          const response = await importUnits({
            data: {
              file: new File([csv], file.name, { type: 'text/csv' })
            }
          })
          message.success(
            `Imported ${response.data.imported_rows ?? rows.length} units.`
          )
          await refetch()
        } catch (e: any) {
          message.error(e.response?.data?.message || 'Import failed.')
        }
      },
      error: (error) => message.error(`Invalid CSV: ${error.message}`)
    })

    return Upload.LIST_IGNORE
  }

  const COLUMNS = useMemo(() => {
    let columns: ColumnsType<FormsUnitInfo> = [
      {
        title: 'Unit Key',
        dataIndex: 'unit',
        width: 400,
        fixed: 'left'
      },
      {
        title: 'Price',
        dataIndex: 'price',
        width: 300,
        fixed: 'left'
      },
      {
        title: 'Price Type',
        dataIndex: 'price_type',
        width: 300
      },
      {
        title: 'Beta',
        dataIndex: 'beta_end_time',
        width: 300,
        render(v) {
          return !!v ? 'Yes' : 'No'
        }
      },
      {
        title: 'Beta End (UTC+0)',
        dataIndex: 'beta_end_time',
        width: 300
      }
    ]
    if (isManualPrice && type !== 'view') {
      columns = [
        ...columns,
        {
          title: 'Action',
          fixed: 'right',
          width: 120,
          render(value, record) {
            return (
              <Button type="link" onClick={() => setEditingUnit(record)}>
                Edit
              </Button>
            )
          }
        }
      ]
    }

    return columns
  }, [isManualPrice, type])

  return (
    <>
      <ListToolBar
        title={<Typography.Title level={5}>Unit List</Typography.Title>}
        actions={[
          <Space>
            {type !== 'view' && (
              <Upload
                accept=".csv,text/csv"
                beforeUpload={importUnitFile}
                disabled={isImporting}
                showUploadList={false}
              >
                <Button icon={<UploadOutlined />} loading={isImporting}>
                  Import
                </Button>
              </Upload>
            )}
            <Button
              icon={<DownloadOutlined />}
              loading={isExporting}
              onClick={exportUnits}
            >
              Export
            </Button>
          </Space>,
          <Input.Group compact className="list-search-bar-container">
            <Input.Search
              placeholder="Search by Unit Key"
              defaultValue={search}
              style={{ width: 300 }}
              onSearch={(value: string) => {
                setSearch(value ? value.trim() : '')
              }}
              allowClear
            />
          </Input.Group>,
          type !== 'view' && (
            <Button onClick={() => setOpenModal(true)}>Create Unit</Button>
          )
        ]}
      />

      {openModal && (
        <UnitListModal
          skuGroup={skuGroup}
          open={openModal}
          onClose={() => setOpenModal(false)}
        />
      )}

      <EditUnitModal
        unit={editingUnit}
        open={!!editingUnit}
        onClose={() => setEditingUnit(undefined)}
        onSuccess={refetch}
      />

      <Table<FormsUnitInfo> columns={COLUMNS} dataSource={filteredData} />
    </>
  )
}

interface EditUnitModalProps {
  unit?: FormsUnitInfo
  open: boolean
  onClose: () => void
  onSuccess: () => void
}

const EditUnitModal = ({
  unit,
  open,
  onClose,
  onSuccess
}: EditUnitModalProps) => {
  const [unitKey, setUnitKey] = useState('')
  const [price, setPrice] = useState('')
  const {
    mutateAsync: patchUnit,
    isLoading,
    reset
  } = usePatchApiV1DevopsBillingProxyApiV1BillingUnitsUnitKey()

  useEffect(() => {
    setUnitKey(unit?.unit || '')
    setPrice(unit?.price || '')
  }, [unit])

  const close = () => {
    reset()
    onClose()
  }

  return (
    <Modal
      title="Edit Unit"
      centered
      visible={open}
      okText="Save"
      confirmLoading={isLoading}
      okButtonProps={{ disabled: !unitKey || !price }}
      onCancel={close}
      onOk={async () => {
        if (!unit?.unit) {
          return
        }

        try {
          await patchUnit({
            unitKey: unit.unit,
            data: {
              price: price.trim(),
              unit: unitKey.trim()
            }
          })
          message.success('Save successful!')
          onSuccess()
          close()
        } catch (e: any) {
          message.error(e.response?.data?.message)
        }
      }}
    >
      <Typography.Text>Unit Key</Typography.Text>
      <Input
        value={unitKey}
        disabled={isLoading}
        onChange={(e) => setUnitKey(e.target.value.trim())}
      />

      <Typography.Text>Price</Typography.Text>
      <Input
        value={price}
        disabled={isLoading}
        onChange={(e) => setPrice(e.target.value.trim())}
      />
    </Modal>
  )
}
