import { useState, useEffect, useRef, useCallback } from 'react'
import {
  BarChart, Bar, AreaChart, Area, XAxis, YAxis, CartesianGrid, Tooltip,
  ResponsiveContainer, PieChart, Pie, Cell, Legend
} from 'recharts'
import {
  Shield, AlertTriangle, Ban, Clock, Wifi, WifiOff,
  CheckCircle2, XCircle, Activity, CalendarClock, ShieldCheck,
  ShieldX, Radio, TrendingUp, X
} from 'lucide-react'
import '../App.css'

const API_URL = import.meta.env.VITE_API_URL || 'http://localhost:8000'
const WS_URL = API_URL.replace('http', 'ws') + '/ws/alerts'

const SEVERITY_COLORS = { HIGH: '#e24b4a', MEDIUM: '#ba7517', LOW: '#639922' }
const ATTACK_COLORS = { 'DoS': '#7f77dd', 'DDoS': '#e24b4a', 'Brute-Force': '#1d9e75' }
const FALLBACK_COLORS = ['#7f77dd', '#1d9e75', '#d85a30', '#d4537e', '#378add']

// Short beep generated with the Web Audio API -- no external sound
// file needed, used to flag a HIGH severity alert as it arrives.
function playAlertSound() {
  try {
    const ctx = new (window.AudioContext || window.webkitAudioContext)()
    const osc = ctx.createOscillator()
    const gain = ctx.createGain()
    osc.connect(gain)
    gain.connect(ctx.destination)
    osc.frequency.value = 880
    gain.gain.setValueAtTime(0.15, ctx.currentTime)
    gain.gain.exponentialRampToValueAtTime(0.001, ctx.currentTime + 0.3)
    osc.start()
    osc.stop(ctx.currentTime + 0.3)
  } catch {
    // Audio not available (e.g. autoplay blocked) -- fail silently
  }
}

// Reshapes [{time, attack_type, count}, ...] into rows suitable for
// a stacked area chart: [{time: '14:00', DoS: 5, DDoS: 2}, ...]
function pivotTimeseries(raw) {
  const byTime = {}
  const types = new Set()
  raw.forEach(({ time, attack_type, count }) => {
    const label = new Date(time).toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' })
    if (!byTime[time]) byTime[time] = { time: label, _sort: time }
    byTime[time][attack_type] = count
    types.add(attack_type)
  })
  const rows = Object.values(byTime).sort((a, b) => new Date(a._sort) - new Date(b._sort))
  return { rows, types: Array.from(types) }
}

function StatCard({ icon: Icon, label, value, tone }) {
  return (
    <div className={`stat-card tone-${tone}`}>
      <div className="stat-icon"><Icon size={20} /></div>
      <div className="stat-body">
        <div className="stat-value">{value}</div>
        <div className="stat-label">{label}</div>
      </div>
    </div>
  )
}

function SeverityBadge({ severity }) {
  return (
    <span className="badge" style={{
      background: `${SEVERITY_COLORS[severity]}22`,
      color: SEVERITY_COLORS[severity],
      border: `1px solid ${SEVERITY_COLORS[severity]}55`
    }}>
      {severity}
    </span>
  )
}

function VerdictControl({ alert, onVerdict }) {
  if (alert.verdict === 'true_positive') {
    return <span className="verdict-tag verdict-true"><CheckCircle2 size={14} /> Confirmed</span>
  }
  if (alert.verdict === 'false_positive') {
    return <span className="verdict-tag verdict-false"><XCircle size={14} /> False Positive</span>
  }
  return (
    <div className="verdict-actions">
      <button className="btn-verdict btn-true" onClick={() => onVerdict(alert.id, 'true_positive')}>
        <CheckCircle2 size={14} /> Confirm
      </button>
      <button className="btn-verdict btn-false" onClick={() => onVerdict(alert.id, 'false_positive')}>
        <XCircle size={14} /> Dismiss
      </button>
    </div>
  )
}

// Toast stack shown in the corner for HIGH severity alerts
function ToastStack({ toasts, onDismiss }) {
  return (
    <div className="toast-stack">
      {toasts.map(t => (
        <div key={t.id} className="toast">
          <AlertTriangle size={16} />
          <div className="toast-body">
            <strong>{t.attack_type}</strong> from <span className="mono">{t.src_ip}</span>
          </div>
          <button className="toast-close" onClick={() => onDismiss(t.id)}><X size={14} /></button>
        </div>
      ))}
    </div>
  )
}

export default function App() {
  const [alerts, setAlerts] = useState([])
  const [stats, setStats] = useState(null)
  const [summary, setSummary] = useState(null)
  const [timeseries, setTimeseries] = useState({ rows: [], types: [] })
  const [topIps, setTopIps] = useState([])
  const [blocked, setBlocked] = useState([])
  const [connected, setConnected] = useState(false)
  const [toasts, setToasts] = useState([])
  const wsRef = useRef(null)
  const toastId = useRef(0)

  const fetchAll = useCallback(async () => {
    try {
      const [alertsRes, statsRes, blockedRes, summaryRes, tsRes, topRes] = await Promise.all([
        fetch(`${API_URL}/api/alerts?limit=100`).then(r => r.json()),
        fetch(`${API_URL}/api/stats`).then(r => r.json()),
        fetch(`${API_URL}/api/blocked-ips`).then(r => r.json()),
        fetch(`${API_URL}/api/stats/summary`).then(r => r.json()),
        fetch(`${API_URL}/api/stats/timeseries?hours=12`).then(r => r.json()),
        fetch(`${API_URL}/api/stats/top-ips?limit=8`).then(r => r.json())
      ])
      setAlerts(alertsRes.alerts || alertsRes)
      setStats(statsRes)
      setBlocked(blockedRes.blocked_ips || blockedRes)
      setSummary(summaryRes)
      setTimeseries(pivotTimeseries(tsRes.data || []))
      setTopIps(topRes.ips || [])
    } catch (e) {
      console.error('Fetch error', e)
    }
  }, [])

  const pushToast = useCallback((alert) => {
    const id = ++toastId.current
    setToasts(prev => [...prev, { id, ...alert }])
    playAlertSound()
    setTimeout(() => setToasts(prev => prev.filter(t => t.id !== id)), 6000)
  }, [])

  useEffect(() => {
    fetchAll()
    const poll = setInterval(fetchAll, 5000)

    function connectWS() {
      const ws = new WebSocket(WS_URL)
      ws.onopen = () => setConnected(true)
      ws.onclose = () => { setConnected(false); setTimeout(connectWS, 3000) }
      ws.onerror = () => ws.close()
      ws.onmessage = (event) => {
        try {
          const msg = JSON.parse(event.data)
          if (msg.type === 'new_alert') {
            setAlerts(prev => [{ ...msg.data, verdict: 'pending' }, ...prev].slice(0, 100))
            if (msg.data.severity === 'HIGH') pushToast(msg.data)
          }
        } catch {
          // ignore malformed messages
        }
      }
      wsRef.current = ws
    }
    connectWS()

    return () => { clearInterval(poll); wsRef.current?.close() }
  }, [fetchAll, pushToast])

  async function handleVerdict(alertId, verdict) {
    setAlerts(prev => prev.map(a => a.id === alertId ? { ...a, verdict } : a))
    try {
      await fetch(`${API_URL}/api/alerts/${alertId}/verdict`, {
        method: 'PATCH',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({ verdict, reviewed_by: 'admin' })
      })
    } catch (e) {
      console.error('Verdict update failed', e)
    }
  }

  async function handleBlock(ip) {
    try {
      await fetch(`${API_URL}/api/block-ip`, {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({ ip_address: ip, reason: 'Blocked from dashboard' })
      })
      fetchAll()
    } catch (e) {
      console.error('Block failed', e)
    }
  }

  const byType = stats?.by_type || []
  const bySeverity = stats?.by_severity || []
  const pendingCount = alerts.filter(a => !a.verdict || a.verdict === 'pending').length
  const isLive = summary?.data_source?.is_live
  const secondsSinceLast = summary?.data_source?.seconds_since_last_alert

  return (
    <div className="dashboard">
      <ToastStack toasts={toasts} onDismiss={(id) => setToasts(prev => prev.filter(t => t.id !== id))} />

      <header className="topbar">
        <div className="brand">
          <Shield size={22} />
          <span>Smart IDPS</span>
          <span className="brand-sub">Security Operations Dashboard</span>
        </div>
        <div className={`conn-status ${connected ? 'live' : 'down'}`}>
          {connected ? <Wifi size={15} /> : <WifiOff size={15} />}
          {connected ? 'Live' : 'Reconnecting...'}
        </div>
      </header>

      <section className="stats-row">
        <StatCard icon={Activity} label="Total Alerts" value={stats?.total_alerts ?? '—'} tone="blue" />
        <StatCard icon={AlertTriangle} label="High Severity" value={bySeverity.find(s => s.severity === 'HIGH')?.count ?? 0} tone="red" />
        <StatCard icon={Ban} label="Blocked IPs" value={stats?.blocked_ips ?? blocked.length} tone="amber" />
        <StatCard icon={Clock} label="Pending Review" value={pendingCount} tone="purple" />
      </section>

      <section className="stats-row">
        <StatCard icon={CalendarClock} label="Alerts Today" value={summary?.alerts_today ?? '—'} tone="blue" />
        <StatCard icon={ShieldCheck} label="Confirmed Attacks" value={summary?.true_positive ?? '—'} tone="green" />
        <StatCard icon={ShieldX} label="False Positives" value={summary?.false_positive ?? '—'} tone="gray" />
        <div className={`stat-card tone-${isLive ? 'green' : 'red'}`}>
          <div className="stat-icon"><Radio size={20} /></div>
          <div className="stat-body">
            <div className="stat-value">{isLive ? 'Live' : 'Stale'}</div>
            <div className="stat-label">
              Data Source {secondsSinceLast != null ? `(${secondsSinceLast}s ago)` : ''}
            </div>
          </div>
        </div>
      </section>

      <section className="panel">
        <h3><TrendingUp size={16} style={{ verticalAlign: 'middle', marginRight: 6 }} />Attacks Over Time (last 12h)</h3>
        <ResponsiveContainer width="100%" height={220}>
          <AreaChart data={timeseries.rows}>
            <CartesianGrid strokeDasharray="3 3" stroke="var(--border)" />
            <XAxis dataKey="time" tick={{ fill: 'var(--text-secondary)', fontSize: 12 }} />
            <YAxis tick={{ fill: 'var(--text-secondary)', fontSize: 12 }} />
            <Tooltip contentStyle={{ background: 'var(--surface-1)', border: '1px solid var(--border)' }} />
            <Legend wrapperStyle={{ fontSize: 12 }} />
            {timeseries.types.map((type, i) => (
              <Area
                key={type}
                type="monotone"
                dataKey={type}
                stackId="1"
                stroke={ATTACK_COLORS[type] || FALLBACK_COLORS[i % FALLBACK_COLORS.length]}
                fill={ATTACK_COLORS[type] || FALLBACK_COLORS[i % FALLBACK_COLORS.length]}
                fillOpacity={0.35}
              />
            ))}
          </AreaChart>
        </ResponsiveContainer>
      </section>

      <section className="charts-row">
        <div className="panel">
          <h3>Attacks by Type</h3>
          <ResponsiveContainer width="100%" height={220}>
            <BarChart data={byType.map(t => ({ name: t.type, count: t.count }))}>
              <CartesianGrid strokeDasharray="3 3" stroke="var(--border)" />
              <XAxis dataKey="name" tick={{ fill: 'var(--text-secondary)', fontSize: 12 }} />
              <YAxis tick={{ fill: 'var(--text-secondary)', fontSize: 12 }} />
              <Tooltip contentStyle={{ background: 'var(--surface-1)', border: '1px solid var(--border)' }} />
              <Bar dataKey="count" radius={[4, 4, 0, 0]}>
                {byType.map((_, i) => <Cell key={i} fill={FALLBACK_COLORS[i % FALLBACK_COLORS.length]} />)}
              </Bar>
            </BarChart>
          </ResponsiveContainer>
        </div>

        <div className="panel">
          <h3>Distribution by Severity</h3>
          <ResponsiveContainer width="100%" height={220}>
            <PieChart>
              <Pie data={bySeverity.map(s => ({ name: s.severity, value: s.count }))}
                   dataKey="value" nameKey="name" innerRadius={50} outerRadius={80} paddingAngle={3}>
                {bySeverity.map((s, i) => <Cell key={i} fill={SEVERITY_COLORS[s.severity] || '#888'} />)}
              </Pie>
              <Legend wrapperStyle={{ fontSize: 12 }} />
              <Tooltip contentStyle={{ background: 'var(--surface-1)', border: '1px solid var(--border)' }} />
            </PieChart>
          </ResponsiveContainer>
        </div>
      </section>

      <section className="panel">
        <h3>Top Attacking IPs</h3>
        <div className="table-wrap">
          <table>
            <thead>
              <tr>
                <th>Source IP</th>
                <th>Total Alerts</th>
                <th>Confirmed</th>
                <th>Action</th>
              </tr>
            </thead>
            <tbody>
              {topIps.map((ip, i) => (
                <tr key={i}>
                  <td className="mono">{ip.ip}</td>
                  <td>{ip.total}</td>
                  <td>{ip.confirmed}</td>
                  <td>
                    <button className="btn-block" onClick={() => handleBlock(ip.ip)}>
                      <Ban size={13} /> Block
                    </button>
                  </td>
                </tr>
              ))}
              {topIps.length === 0 && (
                <tr><td colSpan={4} className="empty">No data yet</td></tr>
              )}
            </tbody>
          </table>
        </div>
      </section>

      <section className="panel alerts-panel">
        <h3>Live Alert Feed</h3>
        <div className="table-wrap">
          <table>
            <thead>
              <tr>
                <th>Attack Type</th>
                <th>Source IP</th>
                <th>Severity</th>
                <th>Timestamp</th>
                <th>Verdict</th>
                <th>Action</th>
              </tr>
            </thead>
            <tbody>
              {alerts.map((a, i) => (
                <tr key={a.id ?? i}>
                  <td>{a.attack_type}</td>
                  <td className="mono">{a.src_ip}</td>
                  <td><SeverityBadge severity={a.severity} /></td>
                  <td className="mono ts">{String(a.timestamp).replace('T', ' ').slice(0, 19)}</td>
                  <td><VerdictControl alert={a} onVerdict={handleVerdict} /></td>
                  <td>
                    <button className="btn-block" onClick={() => handleBlock(a.src_ip)}>
                      <Ban size={13} /> Block
                    </button>
                  </td>
                </tr>
              ))}
              {alerts.length === 0 && (
                <tr><td colSpan={6} className="empty">No alerts yet</td></tr>
              )}
            </tbody>
          </table>
        </div>
      </section>

      <section className="panel">
        <h3>Blocked IP Addresses ({blocked.length})</h3>
        <div className="blocked-grid">
          {blocked.map((b, i) => (
            <div key={i} className="blocked-chip">
              <Ban size={13} />
              <span className="mono">{b.ip}</span>
              <span className="blocked-reason">{b.reason}</span>
            </div>
          ))}
          {blocked.length === 0 && <div className="empty">No blocked IPs</div>}
        </div>
      </section>
    </div>
  )
}
