import React, { useMemo, useState } from 'react'

import { ZoomOutOutlined } from '@ant-design/icons'
import {
  PromDataAccessor,
  PromQueryGroup,
  TimeSeriesChart,
  TransformNullValue,
  Annotation
} from '@diag-ui/chart'
import {
  Accordion,
  Grid,
  Text,
  Title,
  Group,
  Button,
  ActionIcon,
  Box
} from '@mantine/core'
import dayjs from 'dayjs'
import { useParams } from 'react-router-dom'

import {
  ActivitiesActivityGroup,
  ActivityhubClientActivity
} from '@clinic/client'
import { grafanaQueryRange } from '~/api'
import { AutoRefreshButton } from '~/components/AutoRefreshButton'
import DatePicker from '~/components/DatePicker'
import { useCurCluster, useCurOrg } from '~/hooks/state'
import { Status } from '~/pages/event-center/recent/EventGroupCard'
import { replaceQuery } from '~/pages/reports/shared/utils/promql'
import { eventCenterTelemetry } from '~/telemetry/event-center'

import { isSegmentSchemas, Schemas, schemasMap } from './promql_schema'

interface EventChartsProps {
  eventGroup: ActivitiesActivityGroup
}

export const EventCharts: React.FC<EventChartsProps> = ({ eventGroup }) => {
  const { orgId, cid } = useParams()
  const { curOrg } = useCurOrg()
  const { curCluster } = useCurCluster()
  const isFinished = !eventGroup.status || eventGroup.status === Status.Finished
  const { timeRange, eventAnnotations, zoomOut, reset, refresh, isMax } =
    useEventsTS(eventGroup.activities!, isFinished)
  const schemaGroup = schemasMap[eventGroup?.name!] || schemasMap.default
  const [isLoading, setIsLoading] = useState(false)

  return (
    <>
      <Group className='mb-4'>
        <DatePicker.RangePicker
          disabled
          bordered={false}
          showTime={{ format: 'HH:mm' }}
          format='YYYY-MM-DD HH:mm'
          value={[dayjs.unix(timeRange[0]), dayjs.unix(timeRange[1])]}
        />
        <ActionIcon
          variant='outline'
          color='cc-blue'
          size='lg'
          loading={isLoading}
          onClick={() => {
            zoomOut()
            eventCenterTelemetry.zoomOutChart(
              eventGroup?.context_id!,
              timeRange[0],
              timeRange[1]
            )
          }}
          disabled={isMax}
        >
          <ZoomOutOutlined />
        </ActionIcon>
        <Button
          variant='outline'
          loading={isLoading}
          onClick={() => {
            reset()
            eventCenterTelemetry.resetChart(
              eventGroup?.context_id!,
              timeRange[0],
              timeRange[1]
            )
          }}
        >
          Reset
        </Button>
        {!!eventGroup.status && !isFinished && eventGroup?.rule?.length! > 1 && (
          <AutoRefreshButton
            ghost
            disabled={isLoading}
            loading={isLoading}
            type='primary'
            onRefresh={() => {
              refresh()
            }}
            onClick={() => {
              eventCenterTelemetry.refreshChart(
                eventGroup?.context_id!,
                timeRange[0],
                timeRange[1]
              )
            }}
          />
        )}
      </Group>
      <PromDataAccessor
        fetch={(query, tp) =>
          grafanaQueryRange({
            query: replaceQuery(
              query,
              cid!,
              curOrg?.orgType!,
              curCluster?.deployType,
              (eventGroup?.activities?.[0]?.payload as any)?.instance
            ),
            orgId: orgId!,
            clusterId: cid!,
            start: tp.start_time,
            end: tp.end_time,
            step: tp.step.toString() + 's',
            isCloud: true
          }).then((res) => res.data)
        }
        onBatchLoadingChange={setIsLoading}
        params={{ start_time: timeRange[0], end_time: timeRange[1] }}
      >
        {isSegmentSchemas(schemaGroup) ? (
          <Accordion
            defaultValue={schemaGroup.map((segment) => segment.title)}
            variant='filled'
            multiple
          >
            {schemaGroup.map((segment) => (
              <Accordion.Item
                value={segment.title}
                sx={() => ({ backgroundColor: 'transparent !important' })}
              >
                <Accordion.Control>
                  <Title order={3}>{segment.title}</Title>
                </Accordion.Control>
                <Accordion.Panel>
                  <Charts
                    schemas={segment.schemas}
                    annotations={eventAnnotations}
                  />
                </Accordion.Panel>
              </Accordion.Item>
            ))}
          </Accordion>
        ) : (
          <Box p={16}>
            <Charts schemas={schemaGroup} annotations={eventAnnotations} />
          </Box>
        )}
      </PromDataAccessor>
    </>
  )
}

const Charts: React.FC<{ schemas: Schemas; annotations: Annotation[] }> = ({
  schemas,
  annotations
}) => {
  return (
    <Grid>
      {schemas.map((item, i) => (
        <Grid.Col span={6} key={i}>
          <Text mb='md' fz={16}>
            {item.title}
          </Text>
          <TimeSeriesChart
            nullValue={TransformNullValue.AS_ZERO}
            annotations={annotations}
          >
            <PromQueryGroup queries={item.queries} unit={item.unit} />
            {!!item.subQueries && (
              <PromQueryGroup
                queries={item.subQueries.queries}
                unit={item.subQueries.unit}
                position='right'
              />
            )}
          </TimeSeriesChart>
        </Grid.Col>
      ))}
    </Grid>
  )
}

const ONE_HOUR = 60 * 60
const ONE_WEEK = 7 * 24 * 60 * 60

const useEventsTS = (
  events: ActivityhubClientActivity[],
  isFinished: boolean
) => {
  const defaultTS = useMemo(() => {
    const now = dayjs().unix()
    const firstEvent = events[0]
    const lastEvent = events[events.length - 1]
    return [
      // min(start time -1h, now - 2h)
      Math.min(firstEvent.calibration_time! - ONE_HOUR, now - 2 * ONE_HOUR),
      // if events finished: min(end time + 1, now)
      // else: now
      isFinished ? Math.min(lastEvent.calibration_time! + ONE_HOUR, now) : now
    ]
  }, [events, isFinished])
  const maxTS = useMemo(() => {
    const now = dayjs().unix()
    const firstEvent = events[0]
    const lastEvent = events[events.length - 1]
    return [
      firstEvent.calibration_time! - ONE_WEEK,
      Math.min(lastEvent.calibration_time! + ONE_WEEK, now)
    ]
  }, [events])
  const [timeRange, setTimeRange] = useState<number[]>(defaultTS)
  const isMax = maxTS[0] === timeRange[0] && maxTS[1] === timeRange[1]
  const eventAnnotations = useMemo(() => {
    const evts: Annotation[] = events.map((e) => {
      const ts = e.calibration_time! * 1000
      return {
        type: 'line',
        start: [ts, 'min'],
        end: [ts, 'max'],
        style: {
          lineDash: [4, 4]
        },
        top: true,
        text: {
          content: e.display_name || e.name
        }
      }
    })

    if (isFinished && events.length > 1) {
      const firstEvent = events[0]
      const lastEvent = events[events.length - 1]
      evts.push({
        type: 'region',
        start: [firstEvent.calibration_time! * 1000, 'min'],
        end: [lastEvent.calibration_time! * 1000, 'max'],
        top: true
      } as any)
    }

    return evts
  }, [events, isFinished])

  const zoomOut = () => {
    const diff = timeRange[1] - timeRange[0]
    let newStart = timeRange[0] - diff * 1.3
    let newEnd = timeRange[1] + diff * 1.3
    if (newStart < maxTS[0]) {
      newStart = maxTS[0]
    }
    if (newEnd > maxTS[1]) {
      newEnd = maxTS[1]
    }

    setTimeRange([newStart, newEnd])
  }

  const reset = () => setTimeRange(defaultTS)

  const refresh = () => setTimeRange([defaultTS[0], dayjs().unix()])

  return { timeRange, eventAnnotations, zoomOut, reset, refresh, isMax }
}
