<script setup>
import { computed, onMounted, onUnmounted, ref } from 'vue'
import { useI18n } from 'vue-i18n'
import { useRouter } from 'vue-router'
import { Browser } from '@wailsio/runtime'
import {
  Activity, RefreshCw, Pin, Square, Search, ExternalLink, Cpu, MemoryStick,
  HardDrive, Network, Gauge, ChevronDown, ChevronUp, Hexagon, Zap, FileCode2,
  Coffee, Database, Globe, Boxes, Box
} from 'lucide-vue-next'
import ChartView from '../components/ChartView.vue'
import { call, on } from '../api'

const { t } = useI18n()
const router = useRouter()

const entries = ref([])
const loading = ref(false)
const showSys = ref(localStorage.getItem('cc-pm-sys') === '1')
const nameQ = ref('')
const portQ = ref('')
const busy = ref({})
const metrics = ref(null)
const history = ref([]) // { t, cpu, mem, gpu, recv, sent }
const showCharts = ref(localStorage.getItem('cc-pm-charts') !== '0')
const kindIcons = ref({})
const HISTORY_MAX = 60
let timer = 0
let metricsTimer = 0
let offChanged = null

const SYS_NAMES = ['system', 'svchost.exe', 'lsass.exe', 'wininit.exe', 'services.exe', 'csrss.exe', 'winlogon.exe', 'spoolsv.exe', 'searchindexer.exe', 'memcompression', 'registry']
const isSys = x => !x.id && (SYS_NAMES.includes((x.name || '').toLowerCase()) || /^PID \d+$/.test(x.name || ''))

const KIND_UI = {
  node: { icon: Hexagon, color: '#8cc84b' },
  go: { icon: Zap, color: '#00add8' },
  python: { icon: FileCode2, color: '#ffd343' },
  java: { icon: Coffee, color: '#f89820' },
  php: { icon: FileCode2, color: '#a78bfa' },
  dotnet: { icon: Boxes, color: '#8b5cf6' },
  exe: { icon: Box, color: '#60a5fa' },
  mysql: { icon: Database, color: '#4f9df5' },
  redis: { icon: Database, color: '#f16a5b' },
  nginx: { icon: Globe, color: '#26c795' },
  web: { icon: Globe, color: '#4f9df5' },
  other: { icon: Box, color: 'var(--muted)' }
}
const kindUI = k => KIND_UI[k] || KIND_UI.other
function displayIcon(x) {
  if (x?.icon) return x.icon
  return kindIcons.value[x?.kind] || ''
}

function matchEntry(x) {
  const nq = nameQ.value.trim().toLowerCase()
  if (nq) {
    const hay = [x.name, x.exe, x.cmdline, x.dir].filter(Boolean).join('\n').toLowerCase()
    if (!hay.includes(nq)) return false
  }
  const pq = portQ.value.trim()
  if (pq) {
    const ports = x.ports?.length ? x.ports : (x.port ? [x.port] : [])
    if (!ports.some(p => String(p).includes(pq))) return false
  }
  return true
}

const scanned = computed(() => entries.value.filter(x => !x.id && (showSys.value || !isSys(x)) && matchEntry(x)))
const hiddenCount = computed(() => entries.value.filter(x => !x.id && isSys(x)).length)

const fmtCPU = v => (v >= 10 ? Number(v).toFixed(0) : Number(v).toFixed(1)) + '%'
const fmtMem = v => v >= 1024 ? (v / 1024).toFixed(1) + ' GB' : Number(v).toFixed(0) + ' MB'
const fmtIO = v => v >= 1024 ? (v / 1024).toFixed(1) + ' MB/s' : Number(v).toFixed(0) + ' KB/s'
const fmtNet = v => {
  const n = Number(v) || 0
  if (n >= 1024) return (n / 1024).toFixed(2) + ' MB/s'
  return n.toFixed(1) + ' KB/s'
}

async function load() {
  loading.value = true
  try { entries.value = await call('ListLaunchEntries') } catch { /* ignore */ }
  loading.value = false
}
async function loadMetrics() {
  try {
    const m = await call('GetHostMetrics')
    metrics.value = m
    const point = {
      t: new Date().toLocaleTimeString([], { hour: '2-digit', minute: '2-digit', second: '2-digit' }),
      cpu: Number(m.cpu) || 0,
      mem: Number(m.memPercent) || 0,
      gpu: m.gpu < 0 ? null : Number(m.gpu),
      recv: Number(m.netRecvKBs) || 0,
      sent: Number(m.netSentKBs) || 0
    }
    history.value = [...history.value, point].slice(-HISTORY_MAX)
  } catch { /* ignore */ }
}

function toggleSys() {
  showSys.value = !showSys.value
  localStorage.setItem('cc-pm-sys', showSys.value ? '1' : '0')
}
function toggleCharts() {
  showCharts.value = !showCharts.value
  localStorage.setItem('cc-pm-charts', showCharts.value ? '1' : '0')
}

function openPort(p) {
  const n = Number(p)
  if (!n || n <= 0) return
  try { Browser.OpenURL(`http://127.0.0.1:${n}`) } catch { /* ignore */ }
}

async function pin(x) {
  router.push({ path: '/launchpad', query: { pin: '1', name: x.name || '', kind: x.kind || 'other', port: String(x.port || x.ports?.[0] || 0), dir: x.dir || '', pid: String(x.pid || 0) } })
}

async function stop(x) {
  if (!confirm(t('lpStopConfirm', { name: x.name }))) return
  const key = x.pid
  busy.value[key] = 'stop'
  try { await call('StopLaunchApp', 0, x.pid || 0) } catch { /* ignore */ }
  busy.value[key] = false
  setTimeout(load, 500)
}

function lineOpt(series) {
  return {
    backgroundColor: 'transparent',
    grid: { left: 36, right: 12, top: 24, bottom: 28 },
    tooltip: { trigger: 'axis' },
    legend: { show: series.length > 1, top: 0, textStyle: { color: 'var(--muted)', fontSize: 11 } },
    xAxis: {
      type: 'category',
      data: history.value.map(h => h.t),
      axisLabel: { color: 'var(--muted)', fontSize: 10 },
      axisLine: { lineStyle: { color: 'var(--border)' } }
    },
    yAxis: {
      type: 'value',
      min: 0,
      max: series.some(s => s.unit === '%') ? 100 : undefined,
      axisLabel: { color: 'var(--muted)', fontSize: 10 },
      splitLine: { lineStyle: { color: 'var(--border)', opacity: 0.45 } }
    },
    series: series.map(s => ({
      name: s.name,
      type: 'line',
      smooth: true,
      showSymbol: false,
      areaStyle: { opacity: 0.12 },
      lineStyle: { width: 2, color: s.color },
      itemStyle: { color: s.color },
      data: history.value.map(h => h[s.key] == null ? null : h[s.key])
    }))
  }
}

const cpuChart = computed(() => lineOpt([{ name: 'CPU', key: 'cpu', color: '#5da8ff', unit: '%' }]))
const memChart = computed(() => lineOpt([{ name: t('lpMem'), key: 'mem', color: '#53d6a2', unit: '%' }]))
const gpuChart = computed(() => lineOpt([{ name: 'GPU', key: 'gpu', color: '#f7cb4d', unit: '%' }]))
const netChart = computed(() => lineOpt([
  { name: t('pmNetDown'), key: 'recv', color: '#4fd1a1' },
  { name: t('pmNetUp'), key: 'sent', color: '#a78bfa' }
]))

const gpuAvailable = computed(() => metrics.value && metrics.value.gpu >= 0)

onMounted(async () => {
  try { kindIcons.value = await call('ListKindIcons') || {} } catch { kindIcons.value = {} }
  load()
  loadMetrics()
  timer = setInterval(load, 5000)
  metricsTimer = setInterval(loadMetrics, 2000)
  offChanged = on('launchpad:changed', load)
})
onUnmounted(() => {
  clearInterval(timer)
  clearInterval(metricsTimer)
  offChanged?.()
})
</script>

<template>
  <div class="page port-monitor-page">
    <header class="page-head sticky-head lp-head">
      <div class="lp-head-top">
        <div>
          <h1>{{ t('portMonitor') }}</h1>
          <p>{{ t('portMonitorSubtitle') }}</p>
        </div>
        <div class="lp-tools">
          <button class="btn secondary" @click="toggleCharts">
            <component :is="showCharts ? ChevronUp : ChevronDown" />
            {{ showCharts ? t('pmHideCharts') : t('pmShowCharts') }}
          </button>
          <button class="btn secondary" :disabled="loading" @click="load"><RefreshCw :class="{ spinning: loading }" />{{ t('lpRefresh') }}</button>
        </div>
      </div>
    </header>

    <section class="pm-overview card">
      <h2 class="lp-title"><Activity />{{ t('pmOverview') }}</h2>
      <div class="pm-stats">
        <div class="pm-stat">
          <span class="pm-stat-label"><Cpu />CPU</span>
          <b>{{ metrics ? fmtCPU(metrics.cpu) : '—' }}</b>
        </div>
        <div class="pm-stat">
          <span class="pm-stat-label"><MemoryStick />{{ t('lpMem') }}</span>
          <b>{{ metrics ? fmtCPU(metrics.memPercent) : '—' }}</b>
          <small v-if="metrics">{{ fmtMem(metrics.memUsedMB) }} / {{ fmtMem(metrics.memTotalMB) }}</small>
        </div>
        <div class="pm-stat">
          <span class="pm-stat-label"><Gauge />GPU</span>
          <template v-if="gpuAvailable">
            <b>{{ fmtCPU(metrics.gpu) }}</b>
            <small>{{ metrics.gpuName }} · {{ fmtMem(metrics.gpuMemUsedMB) }} / {{ fmtMem(metrics.gpuMemTotalMB) }}</small>
          </template>
          <b v-else class="muted">{{ t('pmGpuNA') }}</b>
        </div>
        <div class="pm-stat">
          <span class="pm-stat-label"><Network />{{ t('pmBandwidth') }}</span>
          <b v-if="metrics">↓ {{ fmtNet(metrics.netRecvKBs) }} · ↑ {{ fmtNet(metrics.netSentKBs) }}</b>
          <b v-else>—</b>
        </div>
      </div>
      <div v-if="showCharts" class="pm-charts">
        <div class="pm-chart-card"><header>CPU</header><ChartView :option="cpuChart" class="pm-chart" /></div>
        <div class="pm-chart-card"><header>{{ t('lpMem') }}</header><ChartView :option="memChart" class="pm-chart" /></div>
        <div class="pm-chart-card"><header>GPU</header><ChartView :option="gpuChart" class="pm-chart" /></div>
        <div class="pm-chart-card"><header>{{ t('pmBandwidth') }}</header><ChartView :option="netChart" class="pm-chart" /></div>
      </div>
    </section>

    <section class="lp-section">
      <div class="pm-ports-head">
        <h2 class="lp-title"><HardDrive />{{ t('lpScanned') }}<em>{{ scanned.length }}</em></h2>
        <div class="lp-filters">
          <label class="lp-sys-toggle"><input type="checkbox" :checked="showSys" @change="toggleSys" />{{ t('lpShowSys') }}<em v-if="hiddenCount && !showSys">{{ hiddenCount }}</em></label>
          <label class="lp-search"><Search /><input v-model.trim="nameQ" type="search" :placeholder="t('lpSearchName')" /></label>
          <label class="lp-search lp-search-port"><Search /><input v-model.trim="portQ" type="search" inputmode="numeric" :placeholder="t('lpSearchPort')" /></label>
        </div>
      </div>
      <div v-if="!scanned.length" class="lp-empty">{{ t('lpEmptyScan') }}</div>
      <div v-else class="lp-grid">
        <article v-for="x in scanned" :key="'p' + x.pid" class="card lp-card running">
          <header>
            <span class="lp-icon" :style="{ color: kindUI(x.kind).color, background: `color-mix(in srgb, ${kindUI(x.kind).color} 14%, transparent)` }">
              <img v-if="displayIcon(x)" :src="displayIcon(x)" alt="" />
              <component v-else :is="kindUI(x.kind).icon" />
            </span>
            <div class="lp-name"><b :title="x.exe || x.name">{{ x.name }}</b><small>{{ x.kind }} · PID {{ x.pid }}</small></div>
            <i class="lp-dot on" :title="t('lpRunning')" />
          </header>
          <div class="lp-ports">
            <button v-for="p in x.ports.slice(0, 4)" :key="p" type="button" class="lp-port lp-port-link" :title="t('lpOpenPort', { p })" @click="openPort(p)">:{{ p }}<ExternalLink /></button>
            <span v-if="x.ports.length > 4" class="lp-port more">+{{ x.ports.length - 4 }}</span>
          </div>
          <div class="lp-res">
            <span :title="'CPU'"><Cpu />{{ fmtCPU(x.cpu) }}</span>
            <span :title="t('lpMem')"><MemoryStick />{{ fmtMem(x.memMB) }}</span>
            <span :title="'IO'"><HardDrive />{{ fmtIO(x.ioKBs) }}</span>
          </div>
          <p v-if="x.dir || x.cmdline || x.exe" class="lp-meta" :title="x.dir || x.cmdline || x.exe">{{ x.dir || x.cmdline || x.exe }}</p>
          <footer>
            <button class="lp-act" @click="pin(x)"><Pin />{{ t('lpPin') }}</button>
            <span class="lp-gap" />
            <button class="lp-act halt" :disabled="busy[x.pid]" @click="stop(x)"><Square />{{ t('lpStop') }}</button>
          </footer>
        </article>
      </div>
    </section>
  </div>
</template>
