import {
  useCallback,
  useEffect,
  useMemo,
  useReducer,
  useRef,
  useState,
} from 'react'
import { Traffic } from 'tauri-plugin-mihomo-api'

import { debugLog } from '@/utils/debug'
import { TrafficDataSampler, formatTrafficName } from '@/utils/traffic-sampler'

// 引用计数管理器
class ReferenceCounter {
  private count = 0
  private callbacks = new Set<() => void>()

  private notify() {
    this.callbacks.forEach((cb) => cb())
  }

  increment(): () => void {
    this.count++
    debugLog(`[ReferenceCounter] 引用计数增加: ${this.count}`)

    this.notify()

    return () => {
      this.count--
      debugLog(`[ReferenceCounter] 引用计数减少: ${this.count}`)

      this.notify()
    }
  }

  onCountChange(callback: () => void) {
    this.callbacks.add(callback)
    return () => this.callbacks.delete(callback)
  }

  getCount(): number {
    return this.count
  }
}

const WORKER_CONFIG = {
  rawDataMinutes: 10,
  compressedDataMinutes: 60,
  compressionRatio: 5,
  snapshotIntervalMs: 100,
  defaultRangeMinutes: 10,
}

class InlineTrafficMonitor {
  private config = { ...WORKER_CONFIG }
  private sampler = new TrafficDataSampler(this.config)
  private throttleTimer: ReturnType<typeof setTimeout> | null = null
  private currentRange = this.config.defaultRangeMinutes
  private lastTimestamp: number | undefined

  constructor(
    private emit: (snapshot: ITrafficWorkerSnapshotMessage) => void,
  ) {}

  start(rangeMinutes?: number) {
    this.currentRange = rangeMinutes ?? this.currentRange
    this.handle({
      type: 'init',
      config: {
        ...this.config,
        defaultRangeMinutes: this.currentRange,
      },
    })
  }

  stop() {
    if (this.throttleTimer !== null) {
      clearTimeout(this.throttleTimer)
      this.throttleTimer = null
    }
    this.sampler.clear()
    this.lastTimestamp = undefined
  }

  handle(message: TrafficWorkerRequestMessage) {
    switch (message.type) {
      case 'init': {
        this.config = { ...message.config }
        this.sampler = new TrafficDataSampler(this.config)
        this.currentRange = message.config.defaultRangeMinutes
        this.emitSnapshot('init')
        break
      }
      case 'append': {
        const timestamp = message.payload.timestamp ?? Date.now()
        const dataPoint: ITrafficDataPoint = {
          up: message.payload.up || 0,
          down: message.payload.down || 0,
          timestamp,
          name: formatTrafficName(timestamp),
        }

        this.lastTimestamp = timestamp
        this.sampler.addDataPoint(dataPoint)
        this.scheduleSnapshot('append-throttle')
        break
      }
      case 'clear': {
        this.sampler.clear()
        this.lastTimestamp = undefined
        this.emitSnapshot('clear')
        break
      }
      case 'setRange': {
        if (this.currentRange !== message.minutes) {
          this.currentRange = message.minutes
          this.emitSnapshot('range-change')
        }
        break
      }
      case 'requestSnapshot': {
        this.emitSnapshot('request')
        break
      }
      default:
        break
    }
  }

  private emitSnapshot(reason: ITrafficWorkerSnapshotMessage['reason']) {
    const dataPoints = this.sampler.getDataForTimeRange(this.currentRange)
    const availableDataPoints = this.sampler.getDataForTimeRange(
      this.config.compressedDataMinutes,
    )

    this.emit({
      type: 'snapshot',
      dataPoints,
      availableDataPoints,
      samplerStats: this.sampler.getStats(),
      rangeMinutes: this.currentRange,
      lastTimestamp: this.lastTimestamp,
      reason,
    })
  }

  private scheduleSnapshot(reason: ITrafficWorkerSnapshotMessage['reason']) {
    if (this.throttleTimer !== null) return
    this.throttleTimer = setTimeout(() => {
      this.throttleTimer = null
      this.emitSnapshot(reason)
    }, this.config.snapshotIntervalMs)
  }
}

class TrafficWorkerClient {
  private worker: Worker | null = null
  private inlineMonitor: InlineTrafficMonitor | null = null
  private mode: 'worker' | 'inline' | null = null
  private listeners = new Set<
    (snapshot: ITrafficWorkerSnapshotMessage) => void
  >()
  private pendingMessages: TrafficWorkerRequestMessage[] = []
  private ready = false
  private currentRange = WORKER_CONFIG.defaultRangeMinutes

  start(rangeMinutes?: number) {
    if (typeof window === 'undefined') {
      debugLog('[TrafficWorkerClient] Window not available, skip start')
      return
    }

    this.currentRange = rangeMinutes ?? this.currentRange

    if (this.ready) return

    const initMessage: TrafficWorkerRequestMessage = {
      type: 'init',
      config: {
        rawDataMinutes: WORKER_CONFIG.rawDataMinutes,
        compressedDataMinutes: WORKER_CONFIG.compressedDataMinutes,
        compressionRatio: WORKER_CONFIG.compressionRatio,
        snapshotIntervalMs: WORKER_CONFIG.snapshotIntervalMs,
        defaultRangeMinutes: this.currentRange,
      },
    }

    if (typeof Worker !== 'undefined') {
      try {
        this.worker = new Worker(
          new URL('../services/traffic-monitor-worker.ts', import.meta.url),
          { type: 'module' },
        )
        this.mode = 'worker'

        this.worker.onmessage = (
          event: MessageEvent<TrafficWorkerResponseMessage>,
        ) => {
          const message = event.data
          if (message.type === 'snapshot') {
            this.listeners.forEach((listener) => listener(message))
          }
        }

        this.worker.onerror = (error) => {
          debugLog(`[TrafficWorkerClient] Worker error: ${String(error)}`)
        }

        this.ready = true
        this.post(initMessage)
        this.flushQueue()
        return
      } catch (error) {
        debugLog(
          `[TrafficWorkerClient] Worker initialization failed, falling back to inline sampler: ${String(error)}`,
        )
        this.worker = null
        this.mode = null
      }
    } else {
      debugLog(
        '[TrafficWorkerClient] Worker not supported, using inline sampler',
      )
    }

    this.startInline(initMessage)
  }

  private startInline(initMessage: TrafficWorkerRequestMessage) {
    this.inlineMonitor = new InlineTrafficMonitor((snapshot) =>
      this.listeners.forEach((listener) => listener(snapshot)),
    )
    this.mode = 'inline'
    this.ready = true
    this.post(initMessage)
    this.flushQueue()
  }

  stop() {
    if (this.worker) {
      this.worker.terminate()
    }
    if (this.inlineMonitor) {
      this.inlineMonitor.stop()
    }
    this.worker = null
    this.inlineMonitor = null
    this.mode = null
    this.ready = false
    this.pendingMessages = []
  }

  onSnapshot(listener: (snapshot: ITrafficWorkerSnapshotMessage) => void) {
    this.listeners.add(listener)
    return () => {
      this.listeners.delete(listener)
    }
  }

  private post(message: TrafficWorkerRequestMessage) {
    if (!this.ready) {
      this.pendingMessages.push(message)
      return
    }

    if (this.mode === 'worker' && this.worker) {
      this.worker.postMessage(message)
      return
    }

    if (this.mode === 'inline' && this.inlineMonitor) {
      this.inlineMonitor.handle(message)
      return
    }

    this.pendingMessages.push(message)
  }

  private flushQueue() {
    if (!this.ready || this.pendingMessages.length === 0) {
      return
    }
    const queued = [...this.pendingMessages]
    this.pendingMessages = []
    queued.forEach((message) => {
      this.post(message)
    })
  }

  private ensureStarted() {
    if (!this.ready) {
      this.start(this.currentRange)
    }
  }

  appendData(traffic: Traffic) {
    this.ensureStarted()
    this.post({
      type: 'append',
      payload: {
        up: traffic?.up ?? 0,
        down: traffic?.down ?? 0,
        timestamp: Date.now(),
      },
    })
  }

  clearData() {
    this.ensureStarted()
    this.post({ type: 'clear' })
  }

  setRange(minutes: number) {
    this.currentRange = minutes
    this.post({ type: 'setRange', minutes })
  }

  requestSnapshot() {
    this.ensureStarted()
    this.post({ type: 'requestSnapshot' })
  }
}

const refCounter = new ReferenceCounter()
let workerClient: TrafficWorkerClient | null = null
const getWorkerClient = () => {
  if (!workerClient) {
    workerClient = new TrafficWorkerClient()
  }
  return workerClient
}

const EMPTY_STATS: ISamplerStats = {
  rawBufferSize: 0,
  compressedBufferSize: 0,
  compressionQueueSize: 0,
  totalMemoryPoints: 0,
}

/**
 * 增强的流量监控Hook - Web Worker驱动的数据采样与压缩
 */
export const useTrafficMonitorEnhanced = (options?: {
  subscribe?: boolean
  enabled?: boolean
}) => {
  const subscribeToSnapshots = options?.subscribe ?? true
  const enabled = options?.enabled ?? true
  const [latestSnapshot, setLatestSnapshot] = useState<{
    availableDataPoints: ITrafficDataPoint[]
    samplerStats: ISamplerStats
    lastTimestamp?: number
  }>({
    availableDataPoints: [],
    samplerStats: EMPTY_STATS,
    lastTimestamp: undefined,
  })
  const [rangeMinutes, setRangeMinutes] = useState(
    WORKER_CONFIG.defaultRangeMinutes,
  )
  const [now, setNow] = useState(() => Date.now())
  const [, forceRefCountRender] = useReducer((value) => value + 1, 0)

  const clientRef = useRef<TrafficWorkerClient | null>(getWorkerClient())
  const currentRangeRef = useRef<number>(WORKER_CONFIG.defaultRangeMinutes)

  // 注册引用计数与Worker生命周期
  useEffect(() => {
    if (!enabled) return

    const client = getWorkerClient()
    clientRef.current = client

    const stopWatchRefCount = refCounter.onCountChange(() =>
      forceRefCountRender(),
    )
    const cleanup = refCounter.increment()
    client.start(currentRangeRef.current)

    let unsubscribe: (() => void) | undefined
    if (subscribeToSnapshots) {
      unsubscribe = client.onSnapshot((message) => {
        setLatestSnapshot({
          availableDataPoints:
            message.availableDataPoints ?? message.dataPoints,
          samplerStats: message.samplerStats,
          lastTimestamp: message.lastTimestamp,
        })
      })

      client.requestSnapshot()
    }

    return () => {
      unsubscribe?.()
      stopWatchRefCount()
      cleanup()
      if (refCounter.getCount() === 0) {
        client.stop()
      }
    }
  }, [enabled, subscribeToSnapshots])

  // Periodically refresh "now" so idle streams age out of the selected window when subscribed
  useEffect(() => {
    if (!enabled || !subscribeToSnapshots) return

    const timer = window.setInterval(() => {
      setNow(Date.now())
    }, 1000)

    return () => window.clearInterval(timer)
  }, [enabled, subscribeToSnapshots])

  // 添加流量数据
  const appendData = useCallback(
    (traffic: Traffic) => {
      if (!enabled) return
      clientRef.current?.appendData(traffic)
    },
    [enabled],
  )

  // 请求不同时间范围的数据
  const requestRange = useCallback(
    (minutes: number) => {
      if (!enabled) return
      currentRangeRef.current = minutes
      setRangeMinutes(minutes)
      clientRef.current?.setRange(minutes)
    },
    [enabled],
  )

  // 清空数据
  const clearData = useCallback(() => {
    if (!enabled) return
    clientRef.current?.clearData()
  }, [enabled])

  const filteredDataPoints = useMemo(() => {
    if (!enabled) return []
    const sourceData = latestSnapshot.availableDataPoints
    if (sourceData.length === 0) return []

    const cutoff = now - rangeMinutes * 60 * 1000
    return sourceData.filter((point) => point.timestamp > cutoff)
  }, [enabled, latestSnapshot.availableDataPoints, rangeMinutes, now])

  return {
    graphData: {
      dataPoints: filteredDataPoints,
      currentRangeMinutes: rangeMinutes,
      requestRange,
      appendData,
      clearData,
    },
    samplerStats: latestSnapshot.samplerStats,
    referenceCount: refCounter.getCount(),
  }
}

/**
 * 图表数据Hook
 */
export const useTrafficGraphDataEnhanced = () => {
  const { graphData, samplerStats, referenceCount } =
    useTrafficMonitorEnhanced()

  return {
    ...graphData,
    samplerStats,
    referenceCount,
  }
}
