import { ref } from 'vue'
import { HUB_URL } from 'src/config/hub'

const THRESHOLDS = {
  meshSignal: { critical: 60, warning: 80 },
  clientSignal: { critical: 50, warning: 70 },
  packetLoss: { critical: 10, warning: 5 },
  backhaulUtil: 80,
  cpu: 80,
  preferred2gChannels: [1, 6, 11],
}

const cache = new Map()
const CACHE_TTL = 120_000

const loading = ref(false)
const error = ref(null)
const data = ref(null)

async function fetchDiagnostic(ip, pass, user = 'superadmin') {
  if (!ip || !pass) { error.value = 'IP et mot de passe requis'; return null }

  const cached = cache.get(ip)
  if (cached && (Date.now() - cached.ts) < CACHE_TTL) {
    data.value = cached.data
    return cached.data
  }

  loading.value = true
  error.value = null

  try {
    const params = new URLSearchParams({ ip, user, pass })
    const res = await fetch(`${HUB_URL}/modem/diagnostic?${params}`)
    const json = await res.json()

    if (!res.ok || json.error) throw new Error(json.error || `HTTP ${res.status}`)

    const processed = processDiagnostic(json)
    cache.set(ip, { data: processed, ts: Date.now() })
    data.value = processed
    return processed
  } catch (e) {
    error.value = e.message
    return null
  } finally {
    loading.value = false
  }
}

function processOnlineStatus(raw) {
  if (!raw.onlineStatus || raw.onlineStatus.error) return null
  return {
    ipv4: raw.onlineStatus.onlineStatusV4 === 'online',
    ipv6: raw.onlineStatus.onlineStatusV6 === 'online',
    uptimeV4: parseInt(raw.onlineStatus.onlineTimeV4) || 0,
  }
}

function processWanIPs(raw) {
  if (!Array.isArray(raw.wanIPs)) return []
  return raw.wanIPs
    .filter(w => w.status === 'Enabled' && w.IPAddress && w.IPAddress !== '0.0.0.0')
    .map(w => ({ ip: w.IPAddress, mask: w.subnetMask, type: w.addressingType, role: classifyIP(w.IPAddress) }))
}

function processRadios(raw) {
  if (!Array.isArray(raw.radios)) return []
  return raw.radios.map(r => ({
    band: r.operatingFrequencyBand,
    channel: parseInt(r.channel) || 0,
    bandwidth: r.currentOperatingChannelBandwidth,
    standard: r.operatingStandards,
    txPower: parseInt(r.transmitPower) || 0,
    autoChannel: r.autoChannelEnable === '1',
    status: r.status,
  }))
}

function processMeshNodes(raw, issues) {
  if (!Array.isArray(raw.meshNodes)) return []
  return raw.meshNodes.map(n => {
    const node = {
      hostname: n.X_TP_HostName || 'unknown',
      model: n.X_TP_ModelName || '',
      ip: n.X_TP_IPAddress,
      mac: n.MACAddress,
      active: n.X_TP_Active === '1',
      isController: n.X_TP_IsController === '1',
      cpu: parseInt(n.X_TP_CPUUsage) || 0,
      uptime: parseInt(n.X_TP_UpTime) || 0,
      firmware: n.softwareVersion || '',
      backhaul: {
        type: n.backhaulLinkType || 'Ethernet',
        signal: parseInt(n.backhaulSignalStrength) || 0,
        utilization: parseInt(n.backhaulLinkUtilization) || 0,
        linkRate: parseInt(n.X_TP_LinkRate) || 0,
      },
      speedUp: parseInt(n.X_TP_UpSpeed) || 0,
      speedDown: parseInt(n.X_TP_DownSpeed) || 0,
    }

    if (!node.isController && node.active && node.backhaul.type === 'Wi-Fi') {
      const sig = node.backhaul.signal
      const name = node.hostname
      if (sig < THRESHOLDS.meshSignal.critical) {
        issues.push({
          severity: 'critical',
          message: `${name}: signal mesh tres faible (${sig})`,
          detail: `Le noeud "${name}" a un signal de backhaul de ${sig}/255. Lien a ${node.backhaul.linkRate} Mbps.`,
          action: `Rapprocher le noeud "${name}" du routeur principal ou ajouter un noeud intermediaire.`,
        })
      } else if (sig < THRESHOLDS.meshSignal.warning) {
        issues.push({
          severity: 'warning',
          message: `${name}: signal mesh faible (${sig})`,
          detail: `Backhaul a ${node.backhaul.linkRate} Mbps, utilisation ${node.backhaul.utilization}%.`,
          action: `Envisager de rapprocher "${name}" du routeur pour ameliorer la vitesse.`,
        })
      }
      if (node.backhaul.utilization > THRESHOLDS.backhaulUtil) {
        issues.push({
          severity: 'warning',
          message: `${name}: backhaul sature (${node.backhaul.utilization}%)`,
          detail: `Le lien entre "${name}" et le routeur est utilise a ${node.backhaul.utilization}%.`,
          action: `Reduire le nombre d'appareils sur ce noeud ou connecter "${name}" en Ethernet.`,
        })
      }
    }
    if (node.cpu > THRESHOLDS.cpu) {
      issues.push({
        severity: 'warning',
        message: `${node.hostname}: CPU eleve (${node.cpu}%)`,
        detail: `Le processeur du noeud est a ${node.cpu}% d'utilisation.`,
        action: `Redemarrer le noeud "${node.hostname}" si le probleme persiste.`,
      })
    }
    return node
  })
}

function processClients(raw, issues, meshNodes, nodeRadios) {
  const clientMap = new Map()

  if (Array.isArray(raw.clients)) {
    for (const c of raw.clients) {
      clientMap.set(c.MACAddress, {
        mac: c.MACAddress,
        hostname: c.X_TP_HostName || '',
        ip: c.X_TP_IPAddress || '',
        signal: parseInt(c.signalStrength) || 0,
        band: '',
        standard: c.operatingStandard || '',
        active: c.active === '1',
        linkDown: parseInt(c.lastDataDownlinkRate) || 0,
        linkUp: parseInt(c.lastDataUplinkRate) || 0,
        maxLinkRate: parseInt(c.X_TP_MaxLinkRate) || 0,
        signalLevel: parseInt(c.X_TP_SignalStrengthLevel) || 0,
        connectedSince: c.associationTime || '',
        apMac: c.X_TP_ApDeviceMac || '',
        radioMac: c.X_TP_RadioMac || '',
        retrans: 0, packetsSent: 0, bytesSent: 0, bytesReceived: 0,
      })
    }
  }

  if (Array.isArray(raw.clientStats)) {
    for (const s of raw.clientStats) {
      const c = clientMap.get(s.MACAddress)
      if (c) {
        c.retrans = parseInt(s.retransCount) || 0
        c.packetsSent = parseInt(s.packetsSent) || 0
        c.bytesSent = parseInt(s.bytesSent) || 0
        c.bytesReceived = parseInt(s.bytesReceived) || 0
        c.speedDown = parseInt(s.X_TP_DownSpeed) || 0
        c.speedUp = parseInt(s.X_TP_UpSpeed) || 0
        if (!c.hostname && s.X_TP_HostName) c.hostname = s.X_TP_HostName
        if (!c.signal && s.signalStrength) c.signal = parseInt(s.signalStrength) || 0
      } else {
        clientMap.set(s.MACAddress, {
          mac: s.MACAddress,
          hostname: s.X_TP_HostName || '',
          ip: s.X_TP_IPAddress || '',
          signal: parseInt(s.signalStrength) || 0,
          standard: s.X_TP_OperatingStandard || '',
          active: true,
          retrans: parseInt(s.retransCount) || 0,
          packetsSent: parseInt(s.packetsSent) || 0,
          bytesSent: parseInt(s.bytesSent) || 0,
          bytesReceived: parseInt(s.bytesReceived) || 0,
          speedDown: parseInt(s.X_TP_DownSpeed) || 0,
          speedUp: parseInt(s.X_TP_UpSpeed) || 0,
          linkDown: parseInt(s.lastDataDownlinkRate) || 0,
          linkUp: parseInt(s.lastDataUplinkRate) || 0,
          maxLinkRate: 0, signalLevel: 0, connectedSince: '', apMac: '', radioMac: '',
        })
      }
    }
  }

  // Assign band from radio MAC to mesh node radios
  if (Array.isArray(nodeRadios)) {
    const radioMap = new Map()
    for (const r of nodeRadios) radioMap.set(r.MACAddress, r.operatingFrequencyBand)
    for (const c of clientMap.values()) {
      if (c.radioMac && radioMap.has(c.radioMac)) c.band = radioMap.get(c.radioMac)
    }
  }

  // Compute loss ratio and generate client issues
  for (const c of clientMap.values()) {
    c.lossPercent = (c.packetsSent > 100 && c.retrans > 0)
      ? Math.round((c.retrans / c.packetsSent) * 1000) / 10
      : 0

    if (c.apMac && meshNodes.length) {
      const node = meshNodes.find(n => n.mac === c.apMac)
      if (node) c.meshNode = node.hostname
    }

    const label = c.hostname || c.mac

    if (c.active && c.signal > 0) {
      if (c.signal < THRESHOLDS.clientSignal.critical) {
        issues.push({
          severity: 'critical',
          message: `${label}: signal tres faible (${c.signal}/255)`,
          detail: `Appareil "${label}" sur ${c.band || '?'}, lien ${Math.round(c.linkDown / 1000)} Mbps.`,
          action: `Rapprocher l'appareil du noeud mesh le plus proche ou verifier les obstacles.`,
        })
      } else if (c.signal < THRESHOLDS.clientSignal.warning) {
        issues.push({
          severity: 'warning',
          message: `${label}: signal faible (${c.signal}/255)`,
          detail: `Vitesse reduite a ${Math.round(c.linkDown / 1000)} Mbps (max ${Math.round(c.maxLinkRate / 1000)} Mbps).`,
          action: `Verifier le placement de l'appareil par rapport au noeud "${c.meshNode || 'principal'}".`,
        })
      }
    }

    if (c.active && c.lossPercent > THRESHOLDS.packetLoss.critical) {
      issues.push({
        severity: 'critical',
        message: `${label}: ${c.lossPercent}% perte de paquets`,
        detail: `${c.retrans} retransmissions sur ${c.packetsSent} paquets envoyes.`,
        action: `Interference probable. Verifier le canal WiFi, les appareils voisins, ou changer la bande.`,
      })
    } else if (c.active && c.lossPercent > THRESHOLDS.packetLoss.warning) {
      issues.push({
        severity: 'warning',
        message: `${label}: ${c.lossPercent}% perte de paquets`,
        detail: `${c.retrans} retransmissions sur ${c.packetsSent} paquets.`,
        action: `Performance reduite. Envisager de changer de canal ou rapprocher l'appareil.`,
      })
    }
  }

  return [...clientMap.values()].sort((a, b) => {
    if (a.active !== b.active) return a.active ? -1 : 1
    return (b.signal || 0) - (a.signal || 0)
  })
}

function checkRadioIssues(radios, issues) {
  for (const r of radios) {
    if (r.band === '2.4GHz' && !r.autoChannel && r.channel > 0
      && THRESHOLDS.preferred2gChannels.indexOf(r.channel) === -1) {
      issues.push({
        severity: 'warning',
        message: `Canal 2.4GHz non optimal (${r.channel})`,
        detail: `Le canal ${r.channel} chevauche les canaux voisins. Les canaux 1, 6 ou 11 sont recommandes.`,
        action: `Changer le canal 2.4GHz a 1, 6 ou 11, ou activer le canal automatique.`,
      })
    }
  }
}

function processDiagnostic(raw) {
  const issues = []
  const online = processOnlineStatus(raw)
  const wanIPs = processWanIPs(raw)
  const radios = processRadios(raw)
  const meshNodes = processMeshNodes(raw, issues)
  const clients = processClients(raw, issues, meshNodes, raw.nodeRadios)
  checkRadioIssues(radios, issues)

  const severityOrder = { critical: 0, warning: 1, info: 2 }
  issues.sort((a, b) => (severityOrder[a.severity] ?? 9) - (severityOrder[b.severity] ?? 9))

  return { fetchedAt: raw.fetchedAt, durationMs: raw.durationMs, issues, meshNodes, clients, radios, wanIPs, online }
}

function classifyIP(ip) {
  if (!ip) return 'unknown'
  const p = ip.split('.').map(Number)
  if (p[0] === 192 && p[1] === 168) return 'lan'
  if (p[0] === 172 && p[1] >= 16 && p[1] <= 31) return 'management'
  if (p[0] === 10) return 'service'
  return 'internet'
}

export function useWifiDiagnostic() {
  return { fetchDiagnostic, loading, error, data }
}
