import { useEffect, useMemo, useState } from "react"
import type { ReactNode } from "react"
import { ArrowDownOutlined, ArrowUpOutlined, DashboardOutlined, HddOutlined } from "@ant-design/icons"
import { Button, Card, Empty, Space, Spin, Typography } from "antd"
import {
  getInstanceRealData,
  getInstanceRealDataHistory,
  type InstanceRealData,
  type ServerDetail,
} from "../../api/servers"

const { Text } = Typography

type InfoMonitorTabProps = {
  server: ServerDetail
  mode?: "summary" | "full"
  onShowMore?: () => void
}

type MonitorSample = {
  key: string
  label: string
  cpu: number
  memory: number
  read: number
  write: number
  inBps: number
  outBps: number
}

type LineSeries = {
  name: string
  color: string
  values: number[]
}

const monitorCache = new Map<number, { realData: InstanceRealData | null; samples: MonitorSample[] }>()
const MONITOR_HISTORY_POINTS = 480

function numberValue(value: number | string | null | undefined) {
  const parsed = Number(value)
  return Number.isFinite(parsed) ? parsed : 0
}

function percent(value: number | string | null | undefined) {
  return Math.max(0, Math.min(100, numberValue(value)))
}

function display(value: unknown, fallback = "-") {
  if (value === null || value === undefined || value === "") return fallback
  return String(value)
}

function formatPercent(value: number) {
  return `${Math.round(value)}%`
}

function formatDisk(value: number | string | null | undefined) {
  const parsed = numberValue(value)
  if (parsed === 0) return "0 MB/s"
  if (parsed < 1) return `${parsed.toFixed(2)} MB/s`
  if (parsed < 10) return `${parsed.toFixed(1)} MB/s`
  return `${Math.round(parsed)} MB/s`
}

function parseBandwidthToBps(value: string | null | undefined) {
  if (!value) return 0
  const match = String(value).trim().match(/^([\d.]+)\s*(bps|kbps|mbps|gbps)$/i)
  if (!match) return 0
  const amount = Number(match[1])
  if (!Number.isFinite(amount)) return 0
  const unit = match[2].toLowerCase()
  if (unit === "gbps") return amount * 1024 * 1024 * 1024
  if (unit === "mbps") return amount * 1024 * 1024
  if (unit === "kbps") return amount * 1024
  return amount
}

function formatBandwidth(value: number) {
  if (value <= 0) return "0bps"
  if (value >= 1024 * 1024) return `${(value / 1024 / 1024).toFixed(1)}Mbps`
  if (value >= 1024) return `${(value / 1024).toFixed(1)}Kbps`
  return `${Math.round(value)}bps`
}

function hourlySampleIndexes(samples: MonitorSample[]) {
  const indexes: number[] = []
  const seen = new Map<string, number>()

  samples.forEach((sample, index) => {
    const date = new Date(sample.key)
    if (Number.isNaN(date.getTime())) {
      indexes.push(index)
      return
    }

    const hourKey = `${date.getFullYear()}-${date.getMonth()}-${date.getDate()}-${date.getHours()}`
    const existingIndex = seen.get(hourKey)
    if (existingIndex === undefined) {
      seen.set(hourKey, indexes.length)
      indexes.push(index)
    } else {
      indexes[existingIndex] = index
    }
  })

  return indexes
}

function formatHourTick(sample: MonitorSample) {
  const date = new Date(sample.key)
  if (Number.isNaN(date.getTime())) return sample.label
  const month = String(date.getMonth() + 1).padStart(2, "0")
  const day = String(date.getDate()).padStart(2, "0")
  const hour = String(date.getHours()).padStart(2, "0")
  return `${month}/${day} ${hour}:00`
}

function sampleFromData(data: InstanceRealData): MonitorSample {
  const date = data.collected_at ? new Date(data.collected_at) : new Date()
  return {
    key: data.collected_at || String(Date.now()),
    label: date.toLocaleTimeString("zh-CN", { hour12: false }),
    cpu: percent(data.cpu_usage),
    memory: percent(data.memory_usage),
    read: numberValue(data.current_read_byte),
    write: numberValue(data.current_write_byte),
    inBps: parseBandwidthToBps(data.current_in_bw),
    outBps: parseBandwidthToBps(data.current_out_bw),
  }
}

function hasRealData(data: InstanceRealData | null): data is InstanceRealData {
  return Boolean(data && Object.keys(data).length > 0)
}

export default function InfoMonitorTab({ server, mode = "full", onShowMore }: InfoMonitorTabProps) {
  const cached = monitorCache.get(server.id)
  const [realData, setRealData] = useState<InstanceRealData | null>(cached?.realData ?? null)
  const [samples, setSamples] = useState<MonitorSample[]>(cached?.samples ?? [])
  const [loading, setLoading] = useState(!cached)

  useEffect(() => {
    const nextCached = monitorCache.get(server.id)
    setRealData(nextCached?.realData ?? null)
    setSamples(nextCached?.samples ?? [])
    setLoading(!nextCached)
  }, [server.id])

  useEffect(() => {
    let mounted = true

    const load = async () => {
      const hasCached = monitorCache.has(server.id)
      if (!hasCached) setLoading(true)
      try {
        const [currentRes, historyRes] = await Promise.all([
          getInstanceRealData(server.id),
          getInstanceRealDataHistory(server.id),
        ])
        const next = currentRes.data.data ?? null
        const history = (historyRes.data.data ?? []).filter(hasRealData)
        if (!mounted) return

        if (!hasRealData(next)) {
          const nextSamples = history.map(sampleFromData).slice(-MONITOR_HISTORY_POINTS)
          monitorCache.set(server.id, { realData: null, samples: nextSamples })
          setRealData(null)
          setSamples(nextSamples)
          return
        }

        const sample = sampleFromData(next)
        const nextSamples = history.map(sampleFromData)
        if (nextSamples.at(-1)?.key !== sample.key) nextSamples.push(sample)
        const slicedSamples = nextSamples.slice(-MONITOR_HISTORY_POINTS)
        monitorCache.set(server.id, { realData: next, samples: slicedSamples })
        setRealData(next)
        setSamples(slicedSamples)
      } catch {
        if (mounted) setRealData(null)
      } finally {
        if (mounted) setLoading(false)
      }
    }

    void load()
    const timer = window.setInterval(load, 15000)
    return () => {
      mounted = false
      window.clearInterval(timer)
    }
  }, [server.id])

  const cpuPercent = percent(realData?.cpu_usage)
  const memoryPercent = percent(realData?.memory_usage)
  const currentRead = numberValue(realData?.current_read_byte)
  const currentWrite = numberValue(realData?.current_write_byte)
  const currentInBps = parseBandwidthToBps(realData?.current_in_bw)
  const currentOutBps = parseBandwidthToBps(realData?.current_out_bw)
  const loadingEmpty = loading && samples.length === 0

  const collectedText = useMemo(() => {
    if (!realData?.collected_at) return "等待采集"
    return new Date(realData.collected_at).toLocaleString("zh-CN", { hour12: false })
  }, [realData?.collected_at])

  if (mode === "summary") {
    return (
      <Card
        variant="outlined"
        title={
          <Space>
            <DashboardOutlined />
            实时监控
          </Space>
        }
        extra={<Button size="small" onClick={onShowMore}>显示更多</Button>}
        styles={{ body: { padding: 16 } }}
      >
        <Space orientation="vertical" size={10} style={{ width: "100%" }}>
          <Space style={{ width: "100%", justifyContent: "space-between" }}>
            <Text type="secondary">{collectedText}</Text>
            <Text type="secondary">{`${formatPercent(cpuPercent)} · ${formatPercent(memoryPercent)}`}</Text>
          </Space>
          {loadingEmpty ? (
            <MonitorLoading height={220} />
          ) : (
            <LibraryLineChart
              unit="%"
              max={100}
              height={220}
              samples={samples}
              valueFormatter={formatPercent}
              series={[
                { name: "CPU", color: "#1677ff", values: samples.map((item) => item.cpu) },
                { name: "内存", color: "#13c2c2", values: samples.map((item) => item.memory) },
              ]}
            />
          )}
        </Space>
      </Card>
    )
  }

  if (loadingEmpty) {
    return (
      <Card variant="outlined" styles={{ body: { padding: 20 } }}>
        <MonitorLoading height={260} />
      </Card>
    )
  }

  return (
    <Space orientation="vertical" size={18} style={{ width: "100%" }}>
      <div
        style={{
          display: "flex",
          justifyContent: "space-between",
          gap: 16,
          alignItems: "center",
          flexWrap: "wrap",
        }}
      >
        <Space>
          <DashboardOutlined />
          <Text strong style={{ fontSize: 16 }}>
            实时监控
          </Text>
        </Space>
        <Space size={12} wrap>
          <Text type="secondary">{collectedText}</Text>
          <Text type="secondary">{display(realData?.kvm)}</Text>
        </Space>
      </div>

      <ChartPanel title="CPU / 内存" summary={`${formatPercent(cpuPercent)} · ${formatPercent(memoryPercent)}`}>
        <LibraryLineChart
          unit="%"
          max={100}
          height={330}
          samples={samples}
          valueFormatter={formatPercent}
          series={[
            { name: "CPU", color: "#1677ff", values: samples.map((item) => item.cpu) },
            { name: "内存", color: "#13c2c2", values: samples.map((item) => item.memory) },
          ]}
        />
      </ChartPanel>

      <ChartPanel
        title="网络"
        summary={`${formatBandwidth(currentInBps)} · ${formatBandwidth(currentOutBps)}`}
        icon={
          <Space size={2}>
            <ArrowDownOutlined />
            <ArrowUpOutlined />
          </Space>
        }
      >
        <LibraryLineChart
          unit="bps"
          height={330}
          samples={samples}
          valueFormatter={formatBandwidth}
          series={[
            { name: "入", color: "#52c41a", values: samples.map((item) => item.inBps) },
            { name: "出", color: "#eb2f96", values: samples.map((item) => item.outBps) },
          ]}
        />
      </ChartPanel>

      <ChartPanel
        title="磁盘读写"
        summary={`${formatDisk(currentRead)} · ${formatDisk(currentWrite)}`}
        icon={<HddOutlined />}
      >
        <LibraryLineChart
          unit="MB/s"
          height={350}
          samples={samples}
          valueFormatter={formatDisk}
          series={[
            { name: "读", color: "#722ed1", values: samples.map((item) => item.read) },
            { name: "写", color: "#fa8c16", values: samples.map((item) => item.write) },
          ]}
        />
      </ChartPanel>
    </Space>
  )
}

function ChartPanel({
  icon,
  title,
  summary,
  children,
}: {
  icon?: ReactNode
  title: string
  summary: string
  children?: ReactNode
}) {
  return (
    <Card variant="outlined" styles={{ body: { padding: 20 } }}>
      <Space orientation="vertical" size={14} style={{ width: "100%" }}>
        <Space style={{ width: "100%", justifyContent: "space-between" }}>
          <Space size={8}>
            {icon}
            <Text strong>{title}</Text>
          </Space>
          <Text type="secondary">{summary}</Text>
        </Space>
        {children}
      </Space>
    </Card>
  )
}

function MonitorLoading({ height }: { height: number }) {
  return (
    <div style={{ height, display: "flex", alignItems: "center", justifyContent: "center" }}>
      <Spin size="large" description="正在加载监控数据" />
    </div>
  )
}

function LibraryLineChart({
  samples,
  series,
  unit,
  max,
  height,
  valueFormatter,
}: {
  samples: MonitorSample[]
  series: LineSeries[]
  unit: string
  max?: number
  height: number
  valueFormatter?: (value: number) => string
}) {
  if (samples.length === 0) {
    return <Empty image={Empty.PRESENTED_IMAGE_SIMPLE} description="等待采集数据" />
  }

  const displayIndexes = hourlySampleIndexes(samples)
  const displaySamples = displayIndexes.map((index) => samples[index])
  const displaySeries = series.map((item) => ({
    ...item,
    values: displayIndexes.map((index) => item.values[index] ?? 0),
  }))
  const allValues = displaySeries.flatMap((item) => item.values)
  const chartMax = Math.max(max ?? 0, ...allValues, 1)
  const formatter = valueFormatter ?? ((value: number) => String(value))

  return (
    <Space orientation="vertical" size={8} style={{ width: "100%" }}>
      <SvgLineChart
        height={height}
        max={chartMax}
        samples={displaySamples}
        series={displaySeries}
        valueFormatter={formatter}
      />
      <Text type="secondary" style={{ alignSelf: "flex-end", fontSize: 12 }}>
        {unit}
      </Text>
    </Space>
  )
}

function SvgLineChart({
  height,
  max,
  samples,
  series,
  valueFormatter,
}: {
  height: number
  max: number
  samples: MonitorSample[]
  series: LineSeries[]
  valueFormatter: (value: number) => string
}) {
  const width = 1000
  const padding = { top: 28, right: 24, bottom: 42, left: 72 }
  const plotWidth = width - padding.left - padding.right
  const plotHeight = Math.max(80, height - padding.top - padding.bottom)
  const safeMax = Math.max(1, max)
  const xFor = (index: number) => {
    if (samples.length <= 1) return padding.left + plotWidth / 2
    return padding.left + (plotWidth * index) / (samples.length - 1)
  }
  const yFor = (value: number) => padding.top + plotHeight * (1 - Math.max(0, value) / safeMax)
  const yTicks = [0, 0.25, 0.5, 0.75, 1].map((ratio) => ({
    key: ratio,
    value: Math.round(safeMax * ratio),
  }))
  const xLabelIndexes = samples.length <= 2
    ? samples.map((_, index) => index)
    : [0, Math.floor((samples.length - 1) / 2), samples.length - 1]

  return (
    <div style={{ width: "100%", height }}>
      <svg role="img" width="100%" height={height} viewBox={`0 0 ${width} ${height}`} preserveAspectRatio="none">
        <rect x={0} y={0} width={width} height={height} fill="#fff" />
        {yTicks.map((tick) => {
          const y = yFor(tick.value)
          return (
            <g key={tick.key}>
              <line x1={padding.left} x2={width - padding.right} y1={y} y2={y} stroke="#edf1f7" strokeWidth={1} />
              <text x={padding.left - 10} y={y + 4} textAnchor="end" fontSize={11} fill="#8c8c8c">
                {valueFormatter(tick.value)}
              </text>
            </g>
          )
        })}
        {xLabelIndexes.map((index) => {
          const sample = samples[index]
          if (!sample) return null
          return (
            <text key={`${sample.key}-${index}`} x={xFor(index)} y={height - 14} textAnchor="middle" fontSize={11} fill="#8c8c8c">
              {formatHourTick(sample)}
            </text>
          )
        })}
        {series.map((item) => {
          const points = item.values
            .map((value, index) => `${xFor(index)},${yFor(value)}`)
            .join(" ")
          return (
            <g key={item.name}>
              <polyline fill="none" stroke={item.color} strokeWidth={2.5} strokeLinejoin="round" strokeLinecap="round" points={points} />
              {item.values.map((value, index) => (
                <circle key={`${item.name}-${index}`} cx={xFor(index)} cy={yFor(value)} r={2.5} fill={item.color}>
                  <title>{`${item.name} ${formatHourTick(samples[index])}: ${valueFormatter(value)}`}</title>
                </circle>
              ))}
            </g>
          )
        })}
      </svg>
      <Space size={14} wrap style={{ justifyContent: "center", width: "100%", marginTop: -24 }}>
        {series.map((item) => (
          <Space key={item.name} size={6}>
            <span style={{ width: 10, height: 10, borderRadius: 2, background: item.color, display: "inline-block" }} />
            <Text type="secondary" style={{ fontSize: 12 }}>{item.name}</Text>
          </Space>
        ))}
      </Space>
    </div>
  )
}
