import { query, queryOne } from '../utils/db'

interface Client {
  ws: any
  interval: number
  timer: ReturnType<typeof setInterval> | null
  autoRefresh: boolean
}

const clients = new Map<any, Client>()

async function getNetworkStatus() {
  // pubnet 상태
  const pubnetStatus = await queryOne(`
    SELECT 
      ps.*,
      pt.name as last_target_name,
      pt.url as last_target_url
    FROM pubnet_status ps
    LEFT JOIN pubnet_targets pt ON ps.last_target_id = pt.id
    WHERE ps.id = 1
  `)
  
  const pubnetLogs = await query(`
    SELECT 
      pl.*,
      pt.name as target_name,
      pt.url as target_url
    FROM pubnet_logs pl
    JOIN pubnet_targets pt ON pl.target_id = pt.id
    ORDER BY pl.checked_at DESC
    LIMIT 5
  `)

  // privnet 상태
  const privnetStatus = await queryOne(`
    SELECT 
      ps.*,
      pt.name as last_target_name,
      pt.url as last_target_url
    FROM privnet_status ps
    LEFT JOIN privnet_targets pt ON ps.last_target_id = pt.id
    WHERE ps.id = 1
  `)
  
  const privnetLogs = await query(`
    SELECT 
      pl.*,
      pt.name as target_name,
      pt.url as target_url
    FROM privnet_logs pl
    JOIN privnet_targets pt ON pl.target_id = pt.id
    ORDER BY pl.checked_at DESC
    LIMIT 5
  `)

  return {
    pubnet: {
      status: pubnetStatus,
      logs: pubnetLogs
    },
    privnet: {
      status: privnetStatus,
      logs: privnetLogs
    },
    timestamp: new Date().toISOString()
  }
}

async function getHistoricalData(datetime: string) {
  const pubnetLogs = await query(`
    SELECT 
      pl.*,
      pt.name as target_name,
      pt.url as target_url
    FROM pubnet_logs pl
    JOIN pubnet_targets pt ON pl.target_id = pt.id
    WHERE pl.checked_at <= $1
    ORDER BY pl.checked_at DESC
    LIMIT 5
  `, [datetime])

  const privnetLogs = await query(`
    SELECT 
      pl.*,
      pt.name as target_name,
      pt.url as target_url
    FROM privnet_logs pl
    JOIN privnet_targets pt ON pl.target_id = pt.id
    WHERE pl.checked_at <= $1
    ORDER BY pl.checked_at DESC
    LIMIT 5
  `, [datetime])

  const pubnetLatest = pubnetLogs[0] || null
  const privnetLatest = privnetLogs[0] || null

  return {
    pubnet: {
      status: pubnetLatest ? {
        is_healthy: pubnetLatest.is_success,
        last_checked_at: pubnetLatest.checked_at,
        last_target_name: pubnetLatest.target_name
      } : null,
      logs: pubnetLogs
    },
    privnet: {
      status: privnetLatest ? {
        is_healthy: privnetLatest.is_success,
        last_checked_at: privnetLatest.checked_at,
        last_target_name: privnetLatest.target_name
      } : null,
      logs: privnetLogs
    },
    timestamp: datetime
  }
}

// 임계값 조회
async function getThresholds() {
  let rows: any[] = []
  try {
    rows = await query(`SELECT category, metric, warning, critical, danger FROM thresholds`)
  } catch (e) {
    // 테이블 없으면 빈 배열
  }
  
  const result: Record<string, Record<string, { warning: number; critical: number; danger: number }>> = {}
  for (const row of rows) {
    if (!result[row.category]) result[row.category] = {}
    result[row.category][row.metric] = { warning: row.warning, critical: row.critical, danger: row.danger }
  }
  return result
}

// 레벨 계산
function getLevel(value: number | null, threshold: { warning: number; critical: number; danger: number }): string {
  if (value === null || value === undefined) return 'normal'
  if (value >= threshold.danger) return 'danger'
  if (value >= threshold.critical) return 'critical'
  if (value >= threshold.warning) return 'warning'
  return 'normal'
}

// 레벨 우선순위
const levelPriority: Record<string, number> = { normal: 0, warning: 1, critical: 2, danger: 3, offline: 4, stopped: 3 }

function getHighestLevel(levels: string[]): string {
  let highest = 'normal'
  for (const level of levels) {
    if ((levelPriority[level] || 0) > (levelPriority[highest] || 0)) {
      highest = level
    }
  }
  return highest
}

// 서버 대시보드 데이터
async function getServerDashboard() {
  const thresholds = await getThresholds()
  const now = new Date()
  const offlineThreshold = 5 * 60 * 1000 // 5분
  
  // 서버 목록
  const servers = await query(`
    SELECT target_id, server_name, server_ip, is_active 
    FROM server_targets 
    WHERE is_active = 1 
    ORDER BY server_name
  `)
  
  const serverStatuses: any[] = []
  const summaryServers = { total: servers.length, normal: 0, warning: 0, critical: 0, danger: 0, offline: 0 }
  const summaryContainers = { total: 0, normal: 0, warning: 0, critical: 0, danger: 0, stopped: 0 }
  
  for (const server of servers) {
    // 최신 스냅샷
    const snapshot = await queryOne(`
      SELECT cpu_percent, memory_percent, memory_total, memory_free, memory_used,
             cpu_temp, load_percent, uptime_str, collected_at
      FROM server_snapshots 
      WHERE target_id = $1 AND is_online = 1
      ORDER BY collected_at DESC 
      LIMIT 1
    `, [server.target_id])
    
    // 최신 디스크 사용률 (최대값) + 용량
    const diskData = await queryOne(`
      SELECT MAX(disk_percent) as disk_percent,
             SUM(disk_used) as disk_used,
             SUM(disk_total) as disk_total
      FROM server_disks
      WHERE target_id = $1
        AND collected_at = (SELECT MAX(collected_at) FROM server_disks WHERE target_id = $1)
        AND device_name NOT LIKE '%loop%'
        AND mount_point NOT LIKE '%/snap%'
        AND fs_type NOT IN ('tmpfs', 'squashfs')
    `, [server.target_id])
    
    // 오프라인 체크
    let isOffline = true
    let lastCollected = null
    if (snapshot && snapshot.collected_at) {
      lastCollected = snapshot.collected_at
      const collectedTime = new Date(snapshot.collected_at).getTime()
      isOffline = (now.getTime() - collectedTime) > offlineThreshold
    }
    
    // 서버 레벨 계산
    let serverLevel = 'offline'
    let cpuLevel = 'normal', memLevel = 'normal', diskLevel = 'normal'
    
    if (!isOffline && snapshot) {
      cpuLevel = getLevel(Number(snapshot.cpu_percent), thresholds.server?.cpu || { warning: 70, critical: 85, danger: 95 })
      // 메모리: snapshot.memory_percent 직접 사용
      memLevel = getLevel(Number(snapshot.memory_percent), thresholds.server?.memory || { warning: 80, critical: 90, danger: 95 })
      diskLevel = getLevel(Number(diskData?.disk_percent), thresholds.server?.disk || { warning: 80, critical: 90, danger: 95 })
      serverLevel = getHighestLevel([cpuLevel, memLevel, diskLevel])
    }
    
    // 요약 집계
    if (serverLevel === 'offline') summaryServers.offline++
    else if (serverLevel === 'danger') summaryServers.danger++
    else if (serverLevel === 'critical') summaryServers.critical++
    else if (serverLevel === 'warning') summaryServers.warning++
    else summaryServers.normal++
    
    // 컨테이너 조회 (최신 데이터만)
    const containers: any[] = []
    const containerSummary = { total: 0, normal: 0, warning: 0, critical: 0, stopped: 0 }
    
    if (!isOffline) {
      // 서버별 최신 컨테이너 (container_name별 최신 1건)
      const latestContainers = await query(`
        SELECT DISTINCT ON (container_name)
          container_name as name,
          container_status as status,
          cpu_percent,
          memory_usage,
          memory_limit,
          uptime,
          network_rx,
          network_tx
        FROM server_containers
        WHERE target_id = $1
        ORDER BY container_name, collected_at DESC
      `, [server.target_id])
      
      for (const c of latestContainers) {
        let containerLevel = 'normal'
        
        if (c.status !== 'running') {
          containerLevel = 'stopped'
          containerSummary.stopped++
        } else {
          const cCpuLevel = getLevel(Number(c.cpu_percent), thresholds.container?.cpu || { warning: 80, critical: 90, danger: 95 })
          const memPct = c.memory_limit ? (Number(c.memory_usage) / Number(c.memory_limit)) * 100 : 0
          const cMemLevel = getLevel(memPct, thresholds.container?.memory || { warning: 80, critical: 90, danger: 95 })
          containerLevel = getHighestLevel([cCpuLevel, cMemLevel])
          
          if (containerLevel === 'danger') containerSummary.critical++
          else if (containerLevel === 'critical') containerSummary.critical++
          else if (containerLevel === 'warning') containerSummary.warning++
          else containerSummary.normal++
        }
        
        containers.push({
          name: c.name,
          status: c.status || 'unknown',
          level: containerLevel,
          cpu_percent: c.cpu_percent,
          memory_usage: c.memory_usage,
          memory_limit: c.memory_limit,
          uptime: c.uptime,
          network_rx: c.network_rx,
          network_tx: c.network_tx
        })
        
        containerSummary.total++
        summaryContainers.total++
      }
      
      // 전체 컨테이너 요약 집계
      summaryContainers.normal += containerSummary.normal
      summaryContainers.warning += containerSummary.warning
      summaryContainers.critical += containerSummary.critical
      summaryContainers.stopped += containerSummary.stopped
    }
    
    serverStatuses.push({
      target_id: server.target_id,
      server_name: server.server_name,
      server_ip: server.server_ip,
      level: serverLevel,
      cpu_percent: snapshot?.cpu_percent ?? null,
      cpu_level: cpuLevel,
      memory_percent: snapshot?.memory_percent ?? null,  // snapshot의 memory_percent 직접 사용
      memory_level: memLevel,
      memory_total: snapshot?.memory_total ?? null,
      memory_free: snapshot?.memory_free ?? null,
      memory_used: snapshot?.memory_used ?? null,
      disk_percent: diskData?.disk_percent ?? null,
      disk_level: diskLevel,
      disk_used: diskData?.disk_used ?? null,
      disk_total: diskData?.disk_total ?? null,
      cpu_temp: snapshot?.cpu_temp ?? null,
      load_percent: snapshot?.load_percent ?? null,
      uptime_str: snapshot?.uptime_str ?? null,
      last_collected: lastCollected,
      containers: containers,
      container_summary: containerSummary
    })
  }
  
  // 서버 정렬: 장애 우선 → 컨테이너 많은 순 → 이름순
  serverStatuses.sort((a, b) => {
    // 1. 장애 여부 (서버 장애 또는 컨테이너에 장애)
    const aHasIssue = a.level !== 'normal' || 
      (a.container_summary.stopped > 0 || a.container_summary.critical > 0 || a.container_summary.warning > 0)
    const bHasIssue = b.level !== 'normal' || 
      (b.container_summary.stopped > 0 || b.container_summary.critical > 0 || b.container_summary.warning > 0)
    
    if (aHasIssue !== bHasIssue) return aHasIssue ? -1 : 1
    
    // 2. 컨테이너 수 (많은 순)
    const aContainers = a.container_summary.total || 0
    const bContainers = b.container_summary.total || 0
    if (aContainers !== bContainers) return bContainers - aContainers
    
    // 3. 서버 이름순
    return a.server_name.localeCompare(b.server_name)
  })
  
  return {
    summary: { servers: summaryServers, containers: summaryContainers },
    servers: serverStatuses,
    timestamp: new Date().toISOString()
  }
}

function startAutoRefresh(client: Client) {
  if (client.timer) {
    clearInterval(client.timer)
  }
  
  if (client.autoRefresh) {
    client.timer = setInterval(async () => {
      const data = await getNetworkStatus()
      client.ws.send(JSON.stringify({ type: 'status', data }))
      
      const serverData = await getServerDashboard()
      client.ws.send(JSON.stringify({ type: 'server', data: serverData }))
    }, client.interval)
  }
}

export default defineWebSocketHandler({
  async open(peer) {
    console.log('[WebSocket] Client connected')
    
    const client: Client = {
      ws: peer,
      interval: 60 * 1000,
      timer: null,
      autoRefresh: true
    }
    
    clients.set(peer, client)
    
    // 초기 데이터 전송
    const data = await getNetworkStatus()
    peer.send(JSON.stringify({ type: 'status', data }))
    
    const serverData = await getServerDashboard()
    peer.send(JSON.stringify({ type: 'server', data: serverData }))
    
    startAutoRefresh(client)
  },
  
  async message(peer, message) {
    const client = clients.get(peer)
    if (!client) return
    
    try {
      const msg = JSON.parse(message.text())
      
      switch (msg.type) {
        case 'set_interval':
          client.interval = msg.interval * 60 * 1000
          console.log(`[WebSocket] Interval changed to ${msg.interval} min`)
          startAutoRefresh(client)
          break
          
        case 'set_auto_refresh':
          client.autoRefresh = msg.enabled
          console.log(`[WebSocket] Auto refresh: ${msg.enabled}`)
          if (msg.enabled) {
            startAutoRefresh(client)
          } else if (client.timer) {
            clearInterval(client.timer)
            client.timer = null
          }
          break
          
        case 'fetch_at':
          const historicalData = await getHistoricalData(msg.datetime)
          peer.send(JSON.stringify({ type: 'historical', data: historicalData }))
          break
          
        case 'refresh':
          const currentData = await getNetworkStatus()
          peer.send(JSON.stringify({ type: 'status', data: currentData }))
          const currentServerData = await getServerDashboard()
          peer.send(JSON.stringify({ type: 'server', data: currentServerData }))
          break
          
        case 'refresh_server':
          const serverDashData = await getServerDashboard()
          peer.send(JSON.stringify({ type: 'server', data: serverDashData }))
          break
      }
    } catch (err) {
      console.error('[WebSocket] Message parse error:', err)
    }
  },
  
  close(peer) {
    console.log('[WebSocket] Client disconnected')
    const client = clients.get(peer)
    if (client?.timer) {
      clearInterval(client.timer)
    }
    clients.delete(peer)
  },
  
  error(peer, error) {
    console.error('[WebSocket] Error:', error)
  }
})
