import { useEffect, useRef, useState } from 'react'
import { ArrowUpCircle, Activity, ScrollText, RefreshCw, TerminalSquare, HardDrive, ChevronDown, ChevronRight, Play } from 'lucide-react'
import { PageShell } from '../components/PageShell'
import type { User } from '../types'

interface AppStatus {
  repo: string
  host: string
  currentCommit: string
  currentCommitAt: string | null
  latestCommit: string
  latestCommitAt: string | null
  updateAvailable: boolean
  commitsBehind: number | null
  deployed: boolean
  checkedAt: string
}

function relativeTime(iso: string | null | undefined): string | null {
  if (!iso) return null
  const diff = Date.now() - new Date(iso).getTime()
  if (diff < 60_000) return 'just now'
  if (diff < 3_600_000) return `${Math.floor(diff / 60_000)}m ago`
  if (diff < 86_400_000) return `${Math.floor(diff / 3_600_000)}h ago`
  if (diff < 7 * 86_400_000) return `${Math.floor(diff / 86_400_000)}d ago`
  return new Date(iso).toLocaleDateString(undefined, { day: 'numeric', month: 'short' })
}

interface StatRange { total: number; used: number }

interface HealthStatus {
  name: string
  host: string
  up: boolean
  ms: number | null
  ram: StatRange | null
  disk: StatRange | null
}

function formatBytes(b: number): string {
  if (b >= 1_073_741_824) return `${(b / 1_073_741_824).toFixed(1)} GB`
  if (b >= 1_048_576)     return `${(b / 1_048_576).toFixed(0)} MB`
  return `${(b / 1024).toFixed(0)} KB`
}

function StatBar({ label, stat }: { label: string; stat: StatRange }) {
  const pct = Math.min(100, Math.round((stat.used / stat.total) * 100))
  const color = pct > 85 ? 'var(--danger)' : pct > 70 ? '#d97706' : '#16a34a'
  return (
    <div style={{ marginTop: '0.45rem' }}>
      <div style={{ display: 'flex', justifyContent: 'space-between', fontSize: '0.68rem', color: 'var(--text-mid)', marginBottom: '0.2rem' }}>
        <span>{label}</span>
        <span>{pct}% · {formatBytes(stat.used)} / {formatBytes(stat.total)}</span>
      </div>
      <div style={{ height: '3px', background: 'var(--card-border)', borderRadius: '2px' }}>
        <div style={{ width: `${pct}%`, height: '100%', background: color, borderRadius: '2px', transition: 'width 0.3s' }} />
      </div>
    </div>
  )
}

interface DeployEntry {
  repo: string
  host: string
  started: string
  finished?: string
  status: 'running' | 'success' | 'failed'
  output?: string
  error?: string
}

interface BackupItem {
  type: 'db' | 'vol'
  name: string
  status: 'ok' | 'error' | 'skipped'
  size: number
  error: string | null
}

interface BackupRun {
  id: string
  started: string
  finished: string
  status: 'success' | 'partial' | 'error'
  items: BackupItem[]
  error: string | null
}

export function AdminMonitor({ user }: { user: User }) {
  const [tab, setTab] = useState<'updates' | 'states' | 'deploys' | 'shell' | 'backups'>('updates')
  const [statuses, setStatuses] = useState<AppStatus[]>([])
  const [deploys, setDeploys] = useState<DeployEntry[]>([])
  const [health, setHealth] = useState<HealthStatus[]>([])
  const [loading, setLoading] = useState(false)
  const [deploying, setDeploying] = useState<Set<string>>(new Set())
  const [rebuilding, setRebuilding] = useState<Set<string>>(new Set())
  const [shellContainer, setShellContainer] = useState('')
  const [shellCmd, setShellCmd] = useState('')
  const [shellOutput, setShellOutput] = useState('')
  const [shellRunning, setShellRunning] = useState(false)
  const termRef = useRef<HTMLPreElement>(null)
  const [backupRuns, setBackupRuns] = useState<BackupRun[]>([])
  const [backupLoading, setBackupLoading] = useState(false)
  const [backupTriggering, setBackupTriggering] = useState(false)
  const [expandedRun, setExpandedRun] = useState<string | null>(null)

  async function fetchStatus(force = false) {
    setLoading(true)
    try {
      const url = force ? '/deploy/status?force=true' : '/deploy/status'
      const res = await fetch(url, { credentials: 'include' })
      if (res.ok) setStatuses(await res.json())
    } finally {
      setLoading(false)
    }
  }

  async function fetchStates() {
    setLoading(true)
    try {
      const res = await fetch('/deploy/health-status', { credentials: 'include' })
      if (res.ok) setHealth(await res.json())
    } finally {
      setLoading(false)
    }
  }

  async function fetchDeploys() {
    setLoading(true)
    try {
      const res = await fetch('/deploy/deploys', { credentials: 'include' })
      if (res.ok) setDeploys(await res.json())
    } finally {
      setLoading(false)
    }
  }

  async function runShellCmd() {
    const cmd = shellCmd.trim()
    if (!shellContainer || !cmd || shellRunning) return
    const [name, host] = shellContainer.split('|')
    setShellOutput(prev => prev + `[${name} @ ${host}] $ ${cmd}\n`)
    setShellRunning(true)
    setShellCmd('')
    try {
      const res = await fetch('/deploy/exec', {
        method: 'POST',
        credentials: 'include',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({ host, command: cmd }),
      })
      const data = await res.json()
      if (!res.ok) {
        setShellOutput(prev => prev + `Error: ${data.error}\n\n`)
      } else {
        const out = [data.stdout, data.stderr].filter(Boolean).join('')
        setShellOutput(prev => prev + (out.trim() || '(no output)') + '\n\n')
      }
    } catch (e: any) {
      setShellOutput(prev => prev + `Request failed: ${e.message}\n\n`)
    } finally {
      setShellRunning(false)
    }
  }

  async function fetchBackupRuns() {
    setBackupLoading(true)
    try {
      const res = await fetch('/deploy/backup/runs', { credentials: 'include' })
      if (res.ok) setBackupRuns(await res.json())
    } finally {
      setBackupLoading(false)
    }
  }

  async function triggerBackup() {
    setBackupTriggering(true)
    try {
      const res = await fetch('/deploy/backup/trigger', { method: 'POST', credentials: 'include' })
      if (res.ok) {
        setTimeout(fetchBackupRuns, 3000)
      }
    } finally {
      setTimeout(() => setBackupTriggering(false), 2000)
    }
  }

  async function triggerDeploy(repo: string) {
    setDeploying(prev => new Set(prev).add(repo))
    try {
      await fetch(`/deploy/deploy/${repo}`, { method: 'POST', credentials: 'include' })
      setTimeout(() => {
        fetchStatus(true)
        setDeploying(prev => { const s = new Set(prev); s.delete(repo); return s })
      }, 3000)
    } catch {
      setDeploying(prev => { const s = new Set(prev); s.delete(repo); return s })
    }
  }

  async function triggerRebuild(repo: string) {
    setRebuilding(prev => new Set(prev).add(repo))
    try {
      await fetch(`/deploy/rebuild/${repo}`, { method: 'POST', credentials: 'include' })
      setTimeout(() => {
        fetchStatus(true)
        setRebuilding(prev => { const s = new Set(prev); s.delete(repo); return s })
      }, 3000)
    } catch {
      setRebuilding(prev => { const s = new Set(prev); s.delete(repo); return s })
    }
  }

  useEffect(() => {
    if (tab === 'updates') fetchStatus()
    if (tab === 'deploys') fetchDeploys()
    if (tab === 'states') {
      fetchStates()
      const id = setInterval(fetchStates, 30_000)
      return () => clearInterval(id)
    }
    if (tab === 'shell' && health.length === 0) fetchStates()
    if (tab === 'backups') fetchBackupRuns()
  }, [tab])

  useEffect(() => {
    if (termRef.current) termRef.current.scrollTop = termRef.current.scrollHeight
  }, [shellOutput])

  const updatesAvailable = statuses.filter(s => s.updateAvailable).length

  const tabs = [
    { key: 'updates' as const, label: 'Updates',    icon: <ArrowUpCircle size={14} strokeWidth={1.75} /> },
    { key: 'states'  as const, label: 'States',     icon: <Activity size={14} strokeWidth={1.75} /> },
    { key: 'deploys' as const, label: 'Deploy Log', icon: <ScrollText size={14} strokeWidth={1.75} /> },
    { key: 'backups' as const, label: 'Backups',    icon: <HardDrive size={14} strokeWidth={1.75} /> },
    { key: 'shell'   as const, label: 'Shell',      icon: <TerminalSquare size={14} strokeWidth={1.75} /> },
  ]

  return (
    <PageShell user={user} padding="2rem">
      <div style={{ flex: 1, display: 'flex', flexDirection: 'column', overflow: 'hidden', minWidth: '900px' }}>

        {/* Tab bar */}
        <div style={{
          display: 'flex', borderBottom: '1px solid var(--card-border)',
          background: 'var(--card-bg)', flexShrink: 0, padding: '0 0.5rem',
        }}>
          {tabs.map(({ key, label, icon }) => (
            <button key={key} onClick={() => setTab(key)} style={{
              background: 'transparent', border: 'none',
              borderBottom: tab === key ? '2px solid var(--gold)' : '2px solid transparent',
              color: tab === key ? 'var(--text-dark)' : 'var(--text-mid)',
              padding: '0.75rem 1.25rem', fontSize: '0.82rem', fontWeight: tab === key ? 600 : 400,
              cursor: 'pointer', display: 'flex', alignItems: 'center', gap: '0.4rem',
              position: 'relative',
            }}>
              {icon}{label}
              {key === 'updates' && updatesAvailable > 0 && (
                <span style={{
                  background: 'var(--gold)', color: 'var(--navy)',
                  borderRadius: '50%', width: '16px', height: '16px',
                  fontSize: '0.6rem', fontWeight: 700,
                  display: 'inline-flex', alignItems: 'center', justifyContent: 'center',
                  marginLeft: '0.15rem',
                }}>{updatesAvailable}</span>
              )}
            </button>
          ))}
        </div>

        {/* Updates tab */}
        {tab === 'updates' && (
          <div style={{ flex: 1, overflowY: 'auto', padding: '1.5rem 2rem' }}>
            <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', marginBottom: '1.25rem' }}>
              <h2 style={{ fontSize: '0.95rem', fontWeight: 600, color: 'var(--text-dark)' }}>App Updates</h2>
              <button onClick={() => fetchStatus(true)} disabled={loading} style={{
                background: 'var(--card-bg)', border: '1px solid var(--card-border)', borderRadius: '6px',
                color: 'var(--text-mid)', padding: '0.35rem 0.9rem', fontSize: '0.8rem',
                display: 'flex', alignItems: 'center', gap: '0.4rem',
              }}>
                <RefreshCw size={13} strokeWidth={1.75} />
                {loading ? 'Checking…' : 'Refresh'}
              </button>
            </div>

            {statuses.length === 0 && !loading && (
              <p style={{ color: 'var(--text-mid)' }}>No apps in deploy map yet.</p>
            )}

            <div style={{ display: 'flex', flexDirection: 'column', gap: '0.6rem' }}>
              {statuses.map(s => (
                <div key={s.repo} style={{
                  background: 'var(--card-bg)',
                  border: '1px solid var(--card-border)',
                  borderLeft: `3px solid ${s.updateAvailable ? 'var(--gold)' : s.deployed ? '#16a34a' : 'var(--card-border)'}`,
                  borderRadius: 'var(--radius)', padding: '0.85rem 1rem',
                  display: 'flex', alignItems: 'center', justifyContent: 'space-between', gap: '1rem',
                  boxShadow: 'var(--shadow-sm)',
                }}>
                  <div>
                    <div style={{ fontWeight: 600, fontSize: '0.88rem', color: 'var(--text-dark)', marginBottom: '0.2rem' }}>
                      {s.repo}
                      {!s.deployed && (
                        <span style={{ marginLeft: '0.5rem', fontSize: '0.7rem', color: 'var(--text-mid)', fontWeight: 400 }}>
                          not deployed
                        </span>
                      )}
                    </div>
                    <div style={{ fontSize: '0.72rem', color: 'var(--text-mid)', fontFamily: 'monospace' }}>
                      {s.deployed ? `deployed: ${s.currentCommit}` : s.currentCommit}
                      {s.deployed && relativeTime(s.currentCommitAt) && (
                        <span style={{ marginLeft: '0.35rem', fontFamily: 'sans-serif', opacity: 0.7 }}>
                          · {relativeTime(s.currentCommitAt)}
                        </span>
                      )}
                      {s.latestCommit !== 'unknown' && (
                        <span style={{ marginLeft: '0.75rem' }}>
                          latest: {s.latestCommit}
                          {relativeTime(s.latestCommitAt) && (
                            <span style={{ marginLeft: '0.35rem', fontFamily: 'sans-serif', opacity: 0.7 }}>
                              · {relativeTime(s.latestCommitAt)}
                            </span>
                          )}
                          {s.commitsBehind != null && s.commitsBehind > 0 && (
                            <span style={{
                              marginLeft: '0.6rem', fontFamily: 'sans-serif',
                              background: 'var(--gold)', color: 'var(--navy)',
                              borderRadius: '3px', padding: '0.05rem 0.35rem',
                              fontSize: '0.65rem', fontWeight: 700,
                            }}>
                              {s.commitsBehind} commit{s.commitsBehind !== 1 ? 's' : ''} behind
                            </span>
                          )}
                        </span>
                      )}
                    </div>
                  </div>
                  <div style={{ flexShrink: 0, display: 'flex', gap: '0.5rem', alignItems: 'center' }}>
                    {s.updateAvailable ? (
                      <button
                        onClick={() => triggerDeploy(s.repo)}
                        disabled={deploying.has(s.repo)}
                        style={{
                          background: 'var(--gold)', color: 'var(--navy)',
                          border: 'none', borderRadius: '6px',
                          padding: '0.4rem 1rem', fontSize: '0.8rem', fontWeight: 700,
                          opacity: deploying.has(s.repo) ? 0.6 : 1,
                        }}
                      >
                        {deploying.has(s.repo) ? 'Updating…' : 'Update'}
                      </button>
                    ) : s.deployed ? (
                      <span style={{ fontSize: '0.75rem', color: '#16a34a', fontWeight: 500 }}>✓ Up to date</span>
                    ) : null}
                    {s.deployed && (
                      <button
                        onClick={() => triggerRebuild(s.repo)}
                        disabled={rebuilding.has(s.repo) || deploying.has(s.repo)}
                        style={{
                          background: 'transparent', color: 'var(--text-mid)',
                          border: '1px solid var(--card-border)', borderRadius: '6px',
                          padding: '0.4rem 0.75rem', fontSize: '0.8rem', fontWeight: 500,
                          opacity: (rebuilding.has(s.repo) || deploying.has(s.repo)) ? 0.5 : 1,
                          cursor: 'pointer',
                        }}
                      >
                        {rebuilding.has(s.repo) ? 'Rebuilding…' : 'Rebuild'}
                      </button>
                    )}
                  </div>
                </div>
              ))}
            </div>
          </div>
        )}

        {/* States tab */}
        {tab === 'states' && (
          <div style={{ flex: 1, overflowY: 'auto', padding: '1.5rem 2rem' }}>
            <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', marginBottom: '1.25rem' }}>
              <h2 style={{ fontSize: '0.95rem', fontWeight: 600, color: 'var(--text-dark)' }}>Service States</h2>
              <button onClick={fetchStates} disabled={loading} style={{
                background: 'var(--card-bg)', border: '1px solid var(--card-border)', borderRadius: '6px',
                color: 'var(--text-mid)', padding: '0.35rem 0.9rem', fontSize: '0.8rem',
                display: 'flex', alignItems: 'center', gap: '0.4rem',
              }}>
                <RefreshCw size={13} strokeWidth={1.75} />
                {loading ? 'Checking…' : 'Refresh'}
              </button>
            </div>
            {health.length === 0 && !loading && (
              <p style={{ color: 'var(--text-mid)' }}>Checking services…</p>
            )}
            <div style={{ display: 'flex', flexDirection: 'column', gap: '0.6rem' }}>
              {health.map(h => (
                <div key={h.name} style={{
                  background: 'var(--card-bg)', border: '1px solid var(--card-border)',
                  borderLeft: `3px solid ${h.up ? '#16a34a' : 'var(--danger)'}`,
                  borderRadius: 'var(--radius)', padding: '0.85rem 1rem',
                  boxShadow: 'var(--shadow-sm)',
                }}>
                  <div style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between' }}>
                    <div style={{ display: 'flex', alignItems: 'center', gap: '0.6rem' }}>
                      <span style={{
                        width: '8px', height: '8px', borderRadius: '50%', flexShrink: 0,
                        background: h.up ? '#16a34a' : 'var(--danger)',
                        boxShadow: h.up ? '0 0 5px #16a34a66' : undefined,
                      }} />
                      <span style={{ fontWeight: 600, fontSize: '0.88rem', color: 'var(--text-dark)' }}>{h.name}</span>
                    </div>
                    <span style={{ fontSize: '0.72rem', fontFamily: 'monospace', color: h.up ? '#16a34a' : 'var(--text-mid)' }}>
                      {h.up ? `${h.ms}ms` : 'unreachable'}
                    </span>
                  </div>
                  {(h.ram || h.disk) && (
                    <div style={{ marginTop: '0.5rem' }}>
                      {h.ram  && <StatBar label="RAM"  stat={h.ram} />}
                      {h.disk && <StatBar label="Disk" stat={h.disk} />}
                    </div>
                  )}
                  {h.up && !h.ram && !h.disk && (
                    <div style={{ marginTop: '0.4rem', fontSize: '0.68rem', color: 'var(--text-mid)' }}>
                      resource stats unavailable
                    </div>
                  )}
                </div>
              ))}
            </div>
          </div>
        )}

        {/* Deploy log tab */}
        {tab === 'deploys' && (
          <div style={{ flex: 1, overflowY: 'auto', padding: '1.5rem 2rem' }}>
            <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', marginBottom: '1.25rem' }}>
              <h2 style={{ fontSize: '0.95rem', fontWeight: 600, color: 'var(--text-dark)' }}>Deploy History</h2>
              <button onClick={fetchDeploys} disabled={loading} style={{
                background: 'var(--card-bg)', border: '1px solid var(--card-border)', borderRadius: '6px',
                color: 'var(--text-mid)', padding: '0.35rem 0.9rem', fontSize: '0.8rem',
                display: 'flex', alignItems: 'center', gap: '0.4rem',
              }}>
                <RefreshCw size={13} strokeWidth={1.75} />
                {loading ? 'Loading…' : 'Refresh'}
              </button>
            </div>
            {deploys.length === 0 && !loading && (
              <p style={{ color: 'var(--text-mid)', fontSize: '0.9rem' }}>No deploys recorded yet.</p>
            )}
            {deploys.map((d, i) => {
              const isSuccess = d.status === 'success'
              const isFailed  = d.status === 'failed'
              return (
                <div key={i} style={{
                  background: 'var(--card-bg)', border: '1px solid var(--card-border)',
                  borderLeft: `3px solid ${isSuccess ? '#16a34a' : isFailed ? 'var(--danger)' : '#d97706'}`,
                  borderRadius: 'var(--radius)', padding: '1rem', marginBottom: '0.6rem',
                  boxShadow: 'var(--shadow-sm)',
                }}>
                  <div style={{ display: 'flex', justifyContent: 'space-between', marginBottom: '0.35rem' }}>
                    <span style={{ fontWeight: 600, fontSize: '0.88rem', color: 'var(--text-dark)' }}>{d.repo}</span>
                    <span style={{
                      fontSize: '0.72rem', fontWeight: 600, padding: '0.15rem 0.5rem', borderRadius: '4px',
                      background: isSuccess ? '#dcfce7' : isFailed ? '#fee2e2' : '#fef9c3',
                      color:      isSuccess ? '#16a34a' : isFailed ? 'var(--danger)' : '#ca8a04',
                    }}>{d.status}</span>
                  </div>
                  <div style={{ fontSize: '0.75rem', color: 'var(--text-mid)', marginBottom: '0.35rem' }}>
                    {d.host} · {new Date(d.started).toLocaleString()}
                    {d.finished && ` → ${new Date(d.finished).toLocaleString()}`}
                  </div>
                  {(d.output || d.error) && (
                    <pre style={{
                      background: '#1e293b', borderRadius: '6px', padding: '0.6rem 0.75rem',
                      fontSize: '0.7rem', color: '#94a3b8', overflowX: 'auto',
                      margin: '0.5rem 0 0', maxHeight: '8rem', overflowY: 'auto',
                    }}>{d.output || d.error}</pre>
                  )}
                </div>
              )
            })}
          </div>
        )}

        {/* Backups tab */}
        {tab === 'backups' && (
          <div style={{ flex: 1, overflowY: 'auto', padding: '1.5rem 2rem' }}>
            <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', marginBottom: '1.25rem' }}>
              <h2 style={{ fontSize: '0.95rem', fontWeight: 600, color: 'var(--text-dark)' }}>Backup Status</h2>
              <div style={{ display: 'flex', gap: '0.6rem' }}>
                <button onClick={fetchBackupRuns} disabled={backupLoading} style={{
                  background: 'var(--card-bg)', border: '1px solid var(--card-border)', borderRadius: '6px',
                  color: 'var(--text-mid)', padding: '0.35rem 0.9rem', fontSize: '0.8rem',
                  display: 'flex', alignItems: 'center', gap: '0.4rem', cursor: 'pointer',
                }}>
                  <RefreshCw size={13} strokeWidth={1.75} />
                  {backupLoading ? 'Loading…' : 'Refresh'}
                </button>
                <button onClick={triggerBackup} disabled={backupTriggering} style={{
                  background: backupTriggering ? 'var(--card-bg)' : 'var(--gold)',
                  color: backupTriggering ? 'var(--text-mid)' : 'var(--navy)',
                  border: '1px solid var(--card-border)', borderRadius: '6px',
                  padding: '0.35rem 0.9rem', fontSize: '0.8rem', fontWeight: 600,
                  display: 'flex', alignItems: 'center', gap: '0.4rem', cursor: backupTriggering ? 'default' : 'pointer',
                }}>
                  <Play size={13} strokeWidth={1.75} />
                  {backupTriggering ? 'Triggered…' : 'Run Now'}
                </button>
              </div>
            </div>

            {/* Summary card */}
            {backupRuns.length > 0 && (() => {
              const last = backupRuns[0]
              const statusColor = last.status === 'success' ? '#16a34a' : last.status === 'partial' ? '#d97706' : 'var(--danger)'
              const durationMs = new Date(last.finished).getTime() - new Date(last.started).getTime()
              const durationStr = durationMs < 60_000
                ? `${Math.round(durationMs / 1000)}s`
                : `${Math.floor(durationMs / 60_000)}m ${Math.round((durationMs % 60_000) / 1000)}s`
              return (
                <div style={{
                  background: 'var(--card-bg)', border: '1px solid var(--card-border)',
                  borderLeft: `3px solid ${statusColor}`,
                  borderRadius: 'var(--radius)', padding: '1rem 1.25rem', marginBottom: '1.25rem',
                  display: 'flex', alignItems: 'center', justifyContent: 'space-between',
                  boxShadow: 'var(--shadow-sm)',
                }}>
                  <div>
                    <div style={{ fontWeight: 600, fontSize: '0.88rem', color: 'var(--text-dark)', marginBottom: '0.2rem' }}>
                      Last backup — <span style={{ color: statusColor, textTransform: 'capitalize' }}>{last.status}</span>
                    </div>
                    <div style={{ fontSize: '0.75rem', color: 'var(--text-mid)' }}>
                      {new Date(last.finished).toLocaleString()} · {durationStr}
                      {last.error && <span style={{ color: 'var(--danger)', marginLeft: '0.5rem' }}>{last.error}</span>}
                    </div>
                  </div>
                  <div style={{ fontSize: '0.78rem', color: 'var(--text-mid)', textAlign: 'right' }}>
                    <div>{last.items.filter(i => i.status === 'ok').length}/{last.items.length} items OK</div>
                    <div style={{ fontSize: '0.7rem', marginTop: '0.1rem' }}>
                      {last.items.filter(i => i.status === 'error').length > 0 &&
                        <span style={{ color: 'var(--danger)' }}>
                          {last.items.filter(i => i.status === 'error').length} error(s)
                        </span>
                      }
                    </div>
                  </div>
                </div>
              )
            })()}

            {backupRuns.length === 0 && !backupLoading && (
              <p style={{ color: 'var(--text-mid)', fontSize: '0.88rem' }}>
                No backup runs recorded yet. Configure Nextcloud in Settings then click Run Now.
              </p>
            )}

            {/* Run history */}
            <div style={{ display: 'flex', flexDirection: 'column', gap: '0.5rem' }}>
              {backupRuns.map(run => {
                const isExpanded = expandedRun === run.id
                const statusColor = run.status === 'success' ? '#16a34a' : run.status === 'partial' ? '#d97706' : 'var(--danger)'
                const durationMs = new Date(run.finished).getTime() - new Date(run.started).getTime()
                const durationStr = durationMs < 60_000
                  ? `${Math.round(durationMs / 1000)}s`
                  : `${Math.floor(durationMs / 60_000)}m ${Math.round((durationMs % 60_000) / 1000)}s`

                return (
                  <div key={run.id} style={{
                    background: 'var(--card-bg)', border: '1px solid var(--card-border)',
                    borderLeft: `3px solid ${statusColor}`,
                    borderRadius: 'var(--radius)', overflow: 'hidden', boxShadow: 'var(--shadow-sm)',
                  }}>
                    <button
                      onClick={() => setExpandedRun(isExpanded ? null : run.id)}
                      style={{
                        width: '100%', background: 'transparent', border: 'none', cursor: 'pointer',
                        padding: '0.75rem 1rem', display: 'flex', alignItems: 'center', justifyContent: 'space-between',
                      }}
                    >
                      <div style={{ display: 'flex', alignItems: 'center', gap: '0.75rem' }}>
                        {isExpanded
                          ? <ChevronDown size={13} strokeWidth={1.75} style={{ color: 'var(--text-mid)', flexShrink: 0 }} />
                          : <ChevronRight size={13} strokeWidth={1.75} style={{ color: 'var(--text-mid)', flexShrink: 0 }} />
                        }
                        <div style={{ textAlign: 'left' }}>
                          <div style={{ fontSize: '0.82rem', fontWeight: 600, color: 'var(--text-dark)' }}>
                            {new Date(run.started).toLocaleString()}
                          </div>
                          <div style={{ fontSize: '0.72rem', color: 'var(--text-mid)', marginTop: '0.1rem' }}>
                            {durationStr} · {run.items.filter(i => i.status === 'ok').length}/{run.items.length} OK
                          </div>
                        </div>
                      </div>
                      <span style={{
                        fontSize: '0.72rem', fontWeight: 600, padding: '0.15rem 0.5rem', borderRadius: '4px',
                        background: run.status === 'success' ? '#dcfce7' : run.status === 'partial' ? '#fef9c3' : '#fee2e2',
                        color: statusColor, textTransform: 'capitalize',
                      }}>{run.status}</span>
                    </button>

                    {isExpanded && (
                      <div style={{ borderTop: '1px solid var(--card-border)', padding: '0.75rem 1rem' }}>
                        {run.items.map((item, idx) => (
                          <div key={idx} style={{
                            display: 'flex', alignItems: 'center', justifyContent: 'space-between',
                            padding: '0.3rem 0', borderBottom: idx < run.items.length - 1 ? '1px solid var(--card-border)' : 'none',
                          }}>
                            <div style={{ display: 'flex', alignItems: 'center', gap: '0.5rem' }}>
                              <span style={{
                                fontSize: '0.65rem', fontWeight: 700, padding: '0.1rem 0.35rem', borderRadius: '3px',
                                background: item.type === 'db' ? '#e0f2fe' : '#f0fdf4',
                                color: item.type === 'db' ? '#0369a1' : '#15803d',
                                textTransform: 'uppercase', letterSpacing: '0.05em',
                              }}>{item.type}</span>
                              <span style={{ fontSize: '0.82rem', color: 'var(--text-dark)', fontFamily: 'monospace' }}>
                                {item.name}
                              </span>
                              {item.error && (
                                <span style={{ fontSize: '0.72rem', color: 'var(--danger)' }}>— {item.error}</span>
                              )}
                            </div>
                            <div style={{ display: 'flex', alignItems: 'center', gap: '0.75rem', flexShrink: 0 }}>
                              {item.size > 0 && (
                                <span style={{ fontSize: '0.72rem', color: 'var(--text-mid)' }}>
                                  {item.size >= 1_048_576
                                    ? `${(item.size / 1_048_576).toFixed(1)} MB`
                                    : `${Math.round(item.size / 1024)} KB`}
                                </span>
                              )}
                              <span style={{
                                fontSize: '0.72rem', fontWeight: 600,
                                color: item.status === 'ok' ? '#16a34a' : item.status === 'skipped' ? '#d97706' : 'var(--danger)',
                              }}>
                                {item.status === 'ok' ? '✓' : item.status === 'skipped' ? '—' : '✗'}
                              </span>
                            </div>
                          </div>
                        ))}
                      </div>
                    )}
                  </div>
                )
              })}
            </div>
          </div>
        )}

        {/* Shell tab */}
        {tab === 'shell' && (
          <div style={{ flex: 1, display: 'flex', flexDirection: 'column', overflow: 'hidden', padding: '1.5rem 2rem' }}>
            <div style={{ display: 'flex', alignItems: 'center', gap: '0.75rem', marginBottom: '1rem', flexShrink: 0 }}>
              <select
                value={shellContainer}
                onChange={e => setShellContainer(e.target.value)}
                style={{
                  background: 'var(--card-bg)', border: '1px solid var(--card-border)',
                  borderRadius: '6px', padding: '0.4rem 0.6rem', fontSize: '0.82rem',
                  color: 'var(--text-dark)', minWidth: '160px',
                }}
              >
                <option value="">Select container</option>
                {health.map(h => (
                  <option key={h.name} value={`${h.name}|${h.host}`}>{h.name}</option>
                ))}
              </select>
              <input
                type="text"
                value={shellCmd}
                onChange={e => setShellCmd(e.target.value)}
                onKeyDown={e => e.key === 'Enter' && runShellCmd()}
                placeholder="e.g. docker compose logs --tail 50"
                disabled={shellRunning}
                style={{
                  flex: 1, background: 'var(--card-bg)', border: '1px solid var(--card-border)',
                  borderRadius: '6px', padding: '0.4rem 0.75rem', fontSize: '0.82rem',
                  color: 'var(--text-dark)', fontFamily: 'monospace',
                }}
              />
              <button
                onClick={runShellCmd}
                disabled={!shellContainer || !shellCmd.trim() || shellRunning}
                style={{
                  background: 'var(--gold)', color: 'var(--navy)', border: 'none',
                  borderRadius: '6px', padding: '0.4rem 1rem', fontSize: '0.82rem', fontWeight: 700,
                  opacity: (!shellContainer || !shellCmd.trim() || shellRunning) ? 0.5 : 1,
                  cursor: (!shellContainer || !shellCmd.trim() || shellRunning) ? 'default' : 'pointer',
                }}
              >
                {shellRunning ? 'Running…' : 'Run'}
              </button>
              {shellOutput && (
                <button
                  onClick={() => setShellOutput('')}
                  style={{
                    background: 'transparent', border: '1px solid var(--card-border)',
                    borderRadius: '6px', padding: '0.4rem 0.75rem', fontSize: '0.8rem',
                    color: 'var(--text-mid)', cursor: 'pointer',
                  }}
                >
                  Clear
                </button>
              )}
            </div>
            <pre
              ref={termRef}
              style={{
                flex: 1, background: '#0f172a', borderRadius: '8px',
                padding: '1rem', fontSize: '0.75rem', color: '#94a3b8',
                overflowY: 'auto', margin: 0, fontFamily: 'monospace',
                whiteSpace: 'pre-wrap', wordBreak: 'break-all',
                border: '1px solid #1e293b',
              }}
            >
              {shellOutput || <span style={{ color: '#475569' }}>Select a container and run a command…</span>}
            </pre>
          </div>
        )}

      </div>
    </PageShell>
  )
}
