import {
  Alert,
  Anchor,
  Badge,
  Box,
  Button,
  Card,
  Code,
  Group,
  Select,
  SimpleGrid,
  Stack,
  TextInput,
  Typography
} from '@tidbcloud/uikit'
import { DatePickerInput, type ComboboxItem } from '@tidbcloud/uikit'
import { MRT_ColumnDef, ProTable } from '@tidbcloud/uikit/biz'
import { IconRefreshCw01 } from '@tidbcloud/uikit/icons'
import type { AxiosError } from 'axios'
import dayjs from 'dayjs'
import utc from 'dayjs/plugin/utc'
import { useCallback, useEffect, useMemo, useRef, useState } from 'react'
import { Link } from 'react-router-dom'

import { jiraControllerListIssuesFromJira } from 'apiClient/services/devops'
import type {
  JiraIssueInJira,
  JiraListIssuesFromJiraResponse
} from 'apiClient/services/devops/interface'
import { useClipboard } from 'utils/dom'

dayjs.extend(utc)

const PAGE_SIZE = 20
const MAX_PAGES = 5
const MAX_ITEMS = 100

const STATUS_OPTIONS: ComboboxItem[] = [
  { value: 'Created', label: 'Created' },
  { value: 'In Progress', label: 'In Progress' },
  { value: 'In Delivery', label: 'In Delivery' },
  { value: 'Done', label: 'Done' },
  { value: 'Fake Alarm', label: 'Fake Alarm' },
  { value: 'No Need Action', label: 'No Need Action' }
]

const PRIORITY_OPTIONS: ComboboxItem[] = [
  { value: 'Warning', label: 'Warning' },
  { value: 'Major', label: 'Major' },
  { value: 'Critical', label: 'Critical' }
]

const formatDateForJql = (value: Date, boundary: 'start' | 'end') =>
  dayjs(value)
    .utc()
    [boundary === 'start' ? 'startOf' : 'endOf']('day')
    .format('YYYY-MM-DD HH:mm')

const escapeJqlValue = (value: string) =>
  value.replace(/\\/g, '\\\\').replace(/"/g, '\\"')

const STATUS_BADGE_COLOR: Record<string, string> = {
  Created: 'gray.6',
  'In Progress': 'blue.6',
  'In Delivery': 'blue.6',
  Done: 'green.6',
  'Fake Alarm': 'green.6',
  'No Need Action': 'green.6'
}

const PRIORITY_BADGE_COLOR: Record<string, string> = {
  Warning: 'yellow.6',
  Major: 'orange.6',
  Critical: 'red.6'
}

const formatIssueDate = (timestamp?: number | null) => {
  if (!timestamp) return '—'
  return dayjs.unix(timestamp).utc().format('YYYY-MM-DD HH:mm:ss')
}

const getStatusBadgeColor = (status: string) =>
  STATUS_BADGE_COLOR[status] ?? 'gray.5'

const getPriorityBadgeColor = (priority: string) =>
  PRIORITY_BADGE_COLOR[priority] ?? 'gray.5'

export type AlertsFilterState = {
  key: string
  status: string
  priority: string
  tenantId: string
  clusterId: string
  createdAfter: Date | null
  createdBefore: Date | null
}

const DEFAULT_FILTERS: AlertsFilterState = {
  key: '',
  status: '',
  priority: '',
  tenantId: '',
  clusterId: '',
  createdAfter: null,
  createdBefore: null
}

export const buildJqlFromFilters = (filters: AlertsFilterState) => {
  const clauses: string[] = []
  clauses.push(`project = "O11Y"`)

  const normalizedStatus = filters.status.trim()
  const normalizedTenant = filters.tenantId.trim()
  const normalizedCluster = filters.clusterId.trim()
  const normalizedKey = filters.key.trim()
  const normalizedPriority = filters.priority.trim()

  if (normalizedKey) {
    clauses.push(`key ~ "${escapeJqlValue(normalizedKey)}"`)
  }

  if (normalizedPriority) {
    clauses.push(`priority = "${escapeJqlValue(normalizedPriority)}"`)
  }

  if (normalizedStatus) {
    clauses.push(`status = "${escapeJqlValue(normalizedStatus)}"`)
  }

  if (normalizedTenant) {
    clauses.push(`"Tenant ID" ~ "${escapeJqlValue(normalizedTenant)}"`)
  }

  if (normalizedCluster) {
    clauses.push(`"Cluster ID" ~ "${escapeJqlValue(normalizedCluster)}"`)
  }

  if (filters.createdAfter) {
    clauses.push(
      `created >= "${formatDateForJql(filters.createdAfter, 'start')}"`
    )
  }

  if (filters.createdBefore) {
    clauses.push(
      `created <= "${formatDateForJql(filters.createdBefore, 'end')}"`
    )
  }

  const base = clauses.join(' AND ')
  const orderBy = 'ORDER BY created DESC'
  return base ? `${base} ${orderBy}` : orderBy
}

const FiltersSection = ({
  draftFilters,
  onChange,
  onApply,
  onReset,
  disabled
}: {
  draftFilters: AlertsFilterState
  onChange: (values: AlertsFilterState) => void
  onApply: () => void
  onReset: () => void
  disabled: boolean
}) => {
  const handleFieldChange = <K extends keyof AlertsFilterState>(
    key: K,
    value: AlertsFilterState[K]
  ) => {
    onChange({ ...draftFilters, [key]: value })
  }

  return (
    <Card withBorder shadow="xs" padding="lg">
      <Stack gap="md">
        <SimpleGrid cols={{ base: 1, sm: 2, lg: 4 }} spacing="md">
          <TextInput
            label="Issue Key"
            placeholder="O11Y-1234"
            value={draftFilters.key}
            onChange={(event) =>
              handleFieldChange('key', event.currentTarget.value)
            }
          />
          <Select
            data={PRIORITY_OPTIONS}
            label="Priority"
            placeholder="All"
            searchable
            clearable
            value={draftFilters.priority || null}
            onChange={(value) => handleFieldChange('priority', value ?? '')}
          />
          <Select
            data={STATUS_OPTIONS}
            label="Status"
            placeholder="All"
            searchable
            clearable
            value={draftFilters.status || null}
            onChange={(value) => handleFieldChange('status', value ?? '')}
          />
          <TextInput
            label="Tenant ID"
            value={draftFilters.tenantId}
            onChange={(event) =>
              handleFieldChange('tenantId', event.currentTarget.value)
            }
          />
          <TextInput
            label="Cluster ID"
            value={draftFilters.clusterId}
            onChange={(event) =>
              handleFieldChange('clusterId', event.currentTarget.value)
            }
          />
          <DatePickerInput
            label="Created After"
            value={draftFilters.createdAfter}
            onChange={(value) => handleFieldChange('createdAfter', value)}
            valueFormat="YYYY-MM-DD"
            clearable
            maxDate={draftFilters.createdBefore ?? undefined}
          />
          <DatePickerInput
            label="Created Before"
            value={draftFilters.createdBefore}
            onChange={(value) => handleFieldChange('createdBefore', value)}
            valueFormat="YYYY-MM-DD"
            clearable
            minDate={draftFilters.createdAfter ?? undefined}
          />
        </SimpleGrid>
        <Group justify="flex-end" gap="sm">
          <Button variant="subtle" onClick={onReset} disabled={disabled}>
            Reset
          </Button>
          <Button onClick={onApply} disabled={disabled}>
            Apply Filters
          </Button>
        </Group>
      </Stack>
    </Card>
  )
}

const AlertsTable = ({
  data,
  isLoading,
  jql,
  onRefresh,
  onLoadMore,
  canLoadMore,
  limitReached
}: {
  data: JiraIssueInJira[]
  isLoading: boolean
  jql: string
  onRefresh: () => void
  onLoadMore: () => void
  canLoadMore: boolean
  limitReached: boolean
}) => {
  const { copied, copy } = useClipboard({ timeout: 2000 })

  const columns = useMemo<MRT_ColumnDef<JiraIssueInJira>[]>(
    () => [
      {
        header: 'Key',
        accessorKey: 'key',
        size: 120,
        Cell: ({ row }) => {
          const key = row.original.key
          if (!key) {
            return (
              <Typography variant="body-sm" c="carbon.5">
                —
              </Typography>
            )
          }
          return (
            <Anchor
              component={Link}
              to={`/aiops/alerts/${key}`}
              target="_blank"
            >
              {key}
            </Anchor>
          )
        }
      },
      {
        header: 'Summary',
        accessorKey: 'summary',
        size: 360,
        Cell: ({ row }) => (
          <Typography variant="body-md" c="carbon.7">
            {row.original.summary || '—'}
          </Typography>
        )
      },
      {
        header: 'Priority',
        accessorKey: 'priority',
        size: 140,
        Cell: ({ row }) => {
          const priority = row.original.priority || 'Unknown'
          return (
            <Badge color={getPriorityBadgeColor(priority)} variant="filled">
              {priority}
            </Badge>
          )
        }
      },
      {
        header: 'Status',
        accessorKey: 'status',
        size: 140,
        Cell: ({ row }) => {
          const status = row.original.status || 'Unknown'
          return (
            <Badge color={getStatusBadgeColor(status)} variant="filled">
              {status}
            </Badge>
          )
        }
      },
      {
        header: 'Created Time',
        accessorKey: 'created_at',
        size: 200,
        Cell: ({ row }) => (
          <Typography variant="body-sm" c="carbon.6">
            {formatIssueDate(row.original.created_at)}
          </Typography>
        )
      },
      {
        header: 'Assignee',
        accessorKey: 'assignee',
        size: 180,
        Cell: ({ row }) => {
          const assignee = row.original.assignee
          if (!assignee?.display_name && !assignee?.email) {
            return (
              <Typography variant="body-sm" c="carbon.6">
                Unassigned
              </Typography>
            )
          }
          return (
            <Stack gap={2}>
              {assignee.display_name && (
                <Typography variant="body-sm" fw={500}>
                  {assignee.display_name}
                </Typography>
              )}
              {assignee.email && (
                <Typography variant="label-sm" c="carbon.6">
                  {assignee.email}
                </Typography>
              )}
            </Stack>
          )
        }
      },
      {
        header: 'Cluster',
        accessorKey: 'customer_info',
        size: 220,
        Cell: ({ row }) => {
          const info = row.original.customer_info
          if (!info?.cluster_id && !info?.tenant_id) {
            return (
              <Typography variant="body-sm" c="carbon.6">
                —
              </Typography>
            )
          }
          return (
            <Stack gap={2}>
              {info.cluster_id && (
                <Typography variant="body-sm">{info.cluster_id}</Typography>
              )}
              {info.tenant_id && (
                <Typography variant="label-sm" c="carbon.6">
                  Tenant: {info.tenant_id}
                </Typography>
              )}
            </Stack>
          )
        }
      }
    ],
    []
  )

  return (
    <Card withBorder shadow="xs" padding="lg">
      <Stack gap="md">
        <Group justify="space-between" align="flex-start">
          <Stack gap={4}>
            <Typography variant="headline-sm">Jira Alerts</Typography>
            <Group gap="xs" align="center">
              <Typography variant="label-sm" c="carbon.6">
                Active JQL
              </Typography>
              <Code>{jql}</Code>
              <Button
                variant="subtle"
                size="xs"
                onClick={() => copy(jql)}
                disabled={!jql}
              >
                {copied ? 'Copied' : 'Copy'}
              </Button>
            </Group>
          </Stack>
          <Button
            variant="default"
            leftSection={<IconRefreshCw01 size={16} />}
            onClick={onRefresh}
            disabled={isLoading}
          >
            Refresh
          </Button>
        </Group>
        <ProTable<JiraIssueInJira>
          columns={columns}
          data={data}
          enableColumnFilters={false}
          enableColumnActions={false}
          enableSorting={false}
          enableDensityToggle={false}
          enableFullScreenToggle={false}
          enableHiding={false}
          enableBottomToolbar={false}
          enableTopToolbar={false}
          loading={isLoading}
          manualPagination={false}
          renderEmptyRowsFallback={() => (
            <Box py="xl" ta="center">
              <Stack align="center" gap={4}>
                <Typography variant="headline-sm">No alerts found</Typography>
                <Typography variant="body-sm" c="carbon.6">
                  Try adjusting your filters or refreshing the data.
                </Typography>
              </Stack>
            </Box>
          )}
          mantineTableProps={{
            horizontalSpacing: 'md',
            verticalSpacing: 'sm'
          }}
        />
        <Stack gap={4} align="stretch">
          <Group justify="space-between" align="center">
            <Typography variant="body-sm" c="carbon.6">
              Showing {data.length} result{data.length === 1 ? '' : 's'}
            </Typography>
            <Button
              variant="default"
              onClick={onLoadMore}
              disabled={isLoading || !canLoadMore || limitReached}
              loading={isLoading && canLoadMore && !limitReached}
            >
              Load More
            </Button>
          </Group>
          {limitReached && (
            <Typography variant="label-sm" c="carbon.6" ta="right">
              Please add filter criteria to narrow down results.
            </Typography>
          )}
          {!limitReached && !canLoadMore && data.length > 0 && (
            <Typography variant="label-sm" c="carbon.6" ta="right">
              All alerts have been loaded.
            </Typography>
          )}
        </Stack>
      </Stack>
    </Card>
  )
}

const cloneFilters = (filters: AlertsFilterState): AlertsFilterState => ({
  key: filters.key,
  status: filters.status,
  priority: filters.priority,
  tenantId: filters.tenantId,
  clusterId: filters.clusterId,
  createdAfter: filters.createdAfter,
  createdBefore: filters.createdBefore
})

const AlertsPage: React.FC = () => {
  const [draftFilters, setDraftFilters] = useState<AlertsFilterState>({
    ...DEFAULT_FILTERS
  })
  const [appliedFilters, setAppliedFilters] = useState<AlertsFilterState>({
    ...DEFAULT_FILTERS
  })
  const [issues, setIssues] = useState<JiraIssueInJira[]>([])
  const [isLoading, setIsLoading] = useState(false)
  const [error, setError] = useState<string | null>(null)
  const [nextPageToken, setNextPageToken] = useState<string | undefined>(
    undefined
  )
  const [pageCount, setPageCount] = useState(0)
  const fetchIdRef = useRef(0)

  const jql = useMemo(
    () => buildJqlFromFilters(appliedFilters),
    [appliedFilters]
  )

  const resetPagination = useCallback(() => {
    fetchIdRef.current += 1
    setIssues([])
    setNextPageToken(undefined)
    setPageCount(0)
    setError(null)
  }, [])

  const fetchIssues = useCallback(
    async ({
      reset = false,
      pageToken
    }: { reset?: boolean; pageToken?: string } = {}) => {
      if (!reset && !pageToken) {
        return
      }
      const requestId = ++fetchIdRef.current
      setIsLoading(true)
      if (reset) {
        setError(null)
      }
      try {
        const params: {
          jql: string
          with_comment: boolean
          per_page: number
          page_token?: string
        } = {
          jql,
          with_comment: false,
          per_page: PAGE_SIZE
        }

        if (!reset && pageToken) {
          params.page_token = pageToken
        }

        const response = await jiraControllerListIssuesFromJira(params)
        const payload: JiraListIssuesFromJiraResponse = response.data

        if (fetchIdRef.current !== requestId) {
          return
        }

        if (payload.errcode && payload.errcode !== 'OK') {
          throw new Error(payload.errmsg || 'Failed to load Jira issues.')
        }

        const newIssues = payload.issues ?? []
        setIssues((prev) => (reset ? newIssues : [...prev, ...newIssues]))
        setNextPageToken(
          payload.is_last ? undefined : payload.next_page_token ?? undefined
        )
        setPageCount((prev) => {
          if (reset) {
            return newIssues.length > 0 ? 1 : 0
          }
          return newIssues.length > 0 ? prev + 1 : prev
        })
      } catch (err) {
        if (fetchIdRef.current !== requestId) {
          return
        }
        const axiosErr = err as AxiosError<{ errmsg?: string }>
        setError(
          axiosErr.response?.data?.errmsg ||
            axiosErr.message ||
            'Failed to fetch Jira issues.'
        )
        if (reset) {
          setIssues([])
          setNextPageToken(undefined)
          setPageCount(0)
        }
      } finally {
        if (fetchIdRef.current === requestId) {
          setIsLoading(false)
        }
      }
    },
    [jql]
  )

  useEffect(() => {
    resetPagination()
    fetchIssues({ reset: true })
  }, [fetchIssues, resetPagination])

  const handleApplyFilters = useCallback(() => {
    resetPagination()
    setAppliedFilters(cloneFilters(draftFilters))
  }, [draftFilters, resetPagination])

  const handleResetFilters = useCallback(() => {
    resetPagination()
    setDraftFilters({ ...DEFAULT_FILTERS })
    setAppliedFilters({ ...DEFAULT_FILTERS })
  }, [resetPagination])

  const handleRefresh = useCallback(() => {
    resetPagination()
    fetchIssues({ reset: true })
  }, [fetchIssues, resetPagination])

  const handleLoadMore = useCallback(() => {
    if (isLoading) return
    if (!nextPageToken) return
    if (pageCount >= MAX_PAGES) return
    if (issues.length >= MAX_ITEMS) return
    fetchIssues({ pageToken: nextPageToken })
  }, [fetchIssues, isLoading, nextPageToken, pageCount, issues.length])

  const limitReached = pageCount >= MAX_PAGES || issues.length >= MAX_ITEMS
  const canLoadMore = Boolean(nextPageToken) && !limitReached

  return (
    <Stack gap="lg">
      <FiltersSection
        draftFilters={draftFilters}
        onChange={setDraftFilters}
        onApply={handleApplyFilters}
        onReset={handleResetFilters}
        disabled={isLoading}
      />

      {error && (
        <Alert color="red" variant="light" title="Failed to load alerts">
          {error}
        </Alert>
      )}

      <AlertsTable
        data={issues}
        isLoading={isLoading}
        jql={jql}
        onRefresh={handleRefresh}
        onLoadMore={handleLoadMore}
        canLoadMore={canLoadMore}
        limitReached={limitReached}
      />
    </Stack>
  )
}

export default AlertsPage
