import { useCallback, useEffect, useRef, useState } from 'react'
import { Loading, ErrorState } from '../../../components/PageState.jsx'
import { useShardFeed } from '../../../lib/useShardFeed.js'
import { describe, kindLabel } from '../../../lib/shardEvents.js'
import { ago } from '../../../lib/format.js'
import { api } from '../../../api/client.js'

// Full live feed from the admin SSE channel — every kind, incl. staff audit,
// cheat detection and login attempts that the public channel never carries.
function AdminLiveFeed() {
  const { events, connected } = useShardFeed({ url: api.adminShardStreamUrl, max: 60 })
  return (
    <section style={{ borderTop: '1px solid var(--line-soft)', paddingTop: 22 }}>
      <div style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between', marginBottom: 12 }}>
        <h3 className="display" style={{ margin: 0, fontSize: '1.05rem', color: 'var(--head)' }}>Live feed (all events)</h3>
        <span className="sans" style={{ display: 'inline-flex', alignItems: 'center', gap: 6, fontSize: '0.74rem', color: connected ? '#7fd0a4' : 'var(--muted)' }}>
          <span style={{ width: 8, height: 8, borderRadius: '50%', background: connected ? '#7fd0a4' : 'var(--dim)' }} />
          {connected ? 'Live' : 'Offline'}
        </span>
      </div>
      {events.length === 0 ? (
        <p className="sans dim" style={{ margin: 0, fontSize: '0.86rem' }}>Waiting for shard events…</p>
      ) : (
        <ul style={{ listStyle: 'none', margin: 0, padding: 0, display: 'flex', flexDirection: 'column', gap: 6, maxHeight: 360, overflowY: 'auto' }}>
          {events.map((e) => (
            <li key={e._id} style={{ display: 'flex', alignItems: 'center', gap: 10, fontSize: '0.85rem' }}>
              <span className="sans" style={{ flex: 'none', fontSize: '0.6rem', letterSpacing: '0.06em', textTransform: 'uppercase', color: 'var(--accent)', minWidth: 92 }}>{kindLabel(e.kind)}</span>
              <span className="sans" style={{ flex: 1, minWidth: 0, color: 'var(--ink)', overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap' }}>{describe(e)}</span>
              <span className="sans dim" style={{ flex: 'none', fontSize: '0.74rem' }}>{ago(e.t)}</span>
            </li>
          ))}
        </ul>
      )}
    </section>
  )
}

// uo-link sidecar control panel. The auth token is write-only over this API —
// stored encrypted, never returned — same convention as the Discord bot token.
// Saving (re)starts the WS ingest client, so Enabled/URL/token changes take
// effect immediately with no redeploy.

function Toggle({ checked, onChange, label }) {
  return (
    <label className="sans" style={{ display: 'inline-flex', alignItems: 'center', gap: 10, cursor: 'pointer', fontSize: '0.9rem', color: 'var(--ink)' }}>
      <input type="checkbox" checked={checked} onChange={(e) => onChange(e.target.checked)} />
      {label}
    </label>
  )
}

const STATUS_COLOR = {
  connected: '#7fd0a4',
  reconnecting: '#e0b070',
  error: '#d98b84',
  disconnected: 'var(--muted)',
}

function StatusPanel({ config }) {
  const color = STATUS_COLOR[config.status] || 'var(--muted)'
  const ingest = config.ingest || {}
  const health = config.health || {}
  return (
    <div style={{ border: '1px solid var(--line)', borderRadius: 10, padding: 16, display: 'flex', flexDirection: 'column', gap: 8 }}>
      <div style={{ display: 'flex', alignItems: 'center', gap: 8 }}>
        <span style={{ width: 9, height: 9, borderRadius: '50%', background: color, boxShadow: `0 0 8px ${color}` }} />
        <span className="sans" style={{ fontSize: '0.9rem', color: 'var(--ink)', textTransform: 'capitalize' }}>
          {config.status || 'disconnected'}
        </span>
      </div>
      {config.statusDetail && (
        <p className="sans" style={{ margin: 0, fontSize: '0.82rem', color: 'var(--muted)' }}>{config.statusDetail}</p>
      )}
      <div className="sans dim" style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: '4px 16px', fontSize: '0.78rem', marginTop: 2 }}>
        <span>Shard link: <strong style={{ color: 'var(--ink)' }}>{config.pluginConnected ? 'up' : 'down'}</strong></span>
        <span>WS ingest: <strong style={{ color: 'var(--ink)' }}>{ingest.connected ? 'connected' : 'offline'}</strong></span>
        <span>Reconnects: <strong style={{ color: 'var(--ink)' }}>{ingest.reconnects ?? 0}</strong></span>
        <span>SSE clients: <strong style={{ color: 'var(--ink)' }}>{(config.sse?.publicClients ?? 0) + (config.sse?.adminClients ?? 0)}</strong></span>
        {config.lastEventAt && <span style={{ gridColumn: '1 / -1' }}>Last event: {new Date(config.lastEventAt).toLocaleString()}</span>}
        {health.uptime && <span style={{ gridColumn: '1 / -1' }}>Sidecar uptime: {health.uptime}</span>}
      </div>
    </div>
  )
}

// ── Town crier ──────────────────────────────────────────────────────────────
function TownCrier() {
  const [id, setId] = useState('')
  const [text, setText] = useState('')
  const [durationSec, setDurationSec] = useState(3600)
  const [busy, setBusy] = useState(false)
  const [msg, setMsg] = useState('')
  const [error, setError] = useState('')

  async function post() {
    setBusy(true); setMsg(''); setError('')
    const lines = text.split('\n').map((l) => l.trim()).filter(Boolean)
    if (!id.trim() || lines.length === 0) {
      setBusy(false)
      return setError('An id and at least one line are required.')
    }
    try {
      await api.admin.postTownCrier({ id: id.trim(), lines, durationSec: Number(durationSec) || undefined })
      setMsg(`Posted “${id.trim()}”.`)
    } catch (err) {
      setError(err.message || 'Could not post.')
    } finally {
      setBusy(false)
    }
  }
  async function remove() {
    if (!id.trim()) return setError('Enter the id to remove.')
    setBusy(true); setMsg(''); setError('')
    try {
      await api.admin.deleteTownCrier(id.trim())
      setMsg(`Removed “${id.trim()}”.`)
    } catch (err) {
      setError(err.message || 'Could not remove.')
    } finally {
      setBusy(false)
    }
  }

  return (
    <section style={{ borderTop: '1px solid var(--line-soft)', paddingTop: 22, display: 'flex', flexDirection: 'column', gap: 12 }}>
      <h3 className="display" style={{ margin: 0, fontSize: '1.05rem', color: 'var(--head)' }}>Town crier</h3>
      <p className="sans" style={{ margin: 0, color: 'var(--muted)', fontSize: '0.86rem', lineHeight: 1.6 }}>
        Broadcast a message that every in-game town crier announces until it expires. Re-posting the same id replaces it.
      </p>
      <label style={{ display: 'block' }}>
        <span className="field-label">Message id</span>
        <input type="text" value={id} onChange={(e) => setId(e.target.value)} className="input" placeholder="news-42" autoComplete="off" style={{ maxWidth: 220 }} />
      </label>
      <label style={{ display: 'block' }}>
        <span className="field-label">Lines (one per line)</span>
        <textarea value={text} onChange={(e) => setText(e.target.value)} className="input" rows={3} placeholder={'Hear ye!\nMarket tax is now 5%.'} style={{ resize: 'vertical' }} />
      </label>
      <label style={{ display: 'block' }}>
        <span className="field-label">Duration (seconds)</span>
        <input type="number" value={durationSec} onChange={(e) => setDurationSec(e.target.value)} className="input" min={1} max={86400} style={{ maxWidth: 160 }} />
      </label>
      <div style={{ display: 'flex', gap: 10, alignItems: 'center' }}>
        <button onClick={post} disabled={busy} className="btn btn-primary btn-sq">{busy ? 'Working…' : 'Post message'}</button>
        <button onClick={remove} disabled={busy} className="btn btn-sq" style={{ borderColor: '#d98b84', color: '#d98b84' }}>Remove by id</button>
        {msg && <span className="sans" style={{ color: '#7fd0a4', fontSize: '0.85rem' }}>{msg}</span>}
        {error && <span className="sans" style={{ color: '#d98b84', fontSize: '0.85rem' }}>{error}</span>}
      </div>
    </section>
  )
}

export default function ShardAdmin() {
  const [config, setConfig] = useState(null)
  const [error, setError] = useState('')
  const [baseUrl, setBaseUrl] = useState('')
  const [wsUrl, setWsUrl] = useState('')
  const [token, setToken] = useState('')
  const [protocol, setProtocol] = useState(3)
  const [enabled, setEnabled] = useState(false)
  const [busy, setBusy] = useState(false)
  const [msg, setMsg] = useState('')
  const [saveError, setSaveError] = useState('')
  const pollRef = useRef(null)
  const initializedRef = useRef(false)

  const load = useCallback(async () => {
    try {
      const c = await api.admin.getUoLinkConfig()
      setConfig(c)
      // Seed the editable fields once; later polls only refresh the status panel
      // so they never clobber what the admin is mid-typing.
      if (!initializedRef.current) {
        setBaseUrl(c.baseUrl || '')
        setWsUrl(c.wsUrl || '')
        setProtocol(c.protocol || 3)
        setEnabled(c.enabled)
        initializedRef.current = true
      }
    } catch {
      setError('Could not load uo-link config.')
    }
  }, [])

  useEffect(() => {
    load()
    pollRef.current = setInterval(load, 5000)
    return () => clearInterval(pollRef.current)
  }, [load])

  async function save() {
    setBusy(true); setMsg(''); setSaveError('')
    try {
      const body = { baseUrl, wsUrl, protocol: Number(protocol), enabled }
      if (token) body.token = token
      const saved = await api.admin.saveUoLinkConfig(body)
      setConfig(saved)
      setToken('')
      setMsg('Saved.')
    } catch (err) {
      setSaveError(err.message || 'Could not save.')
    } finally {
      setBusy(false)
    }
  }

  if (error) return <ErrorState message={error} />
  if (!config) return <Loading />

  return (
    <section style={{ maxWidth: 560, display: 'flex', flexDirection: 'column', gap: 20 }}>
      <h2 className="display" style={{ margin: 0, fontSize: '1.2rem', color: 'var(--head)' }}>Shard (uo-link)</h2>

      <StatusPanel config={config} />

      <Toggle checked={enabled} onChange={setEnabled} label="Enable the shard integration" />

      <label style={{ display: 'block' }}>
        <span className="field-label">Base URL (REST)</span>
        <input type="text" value={baseUrl} onChange={(e) => setBaseUrl(e.target.value)} className="input" autoComplete="off" placeholder="http://127.0.0.1:8080" />
      </label>

      <label style={{ display: 'block' }}>
        <span className="field-label">WebSocket URL (feed)</span>
        <input type="text" value={wsUrl} onChange={(e) => setWsUrl(e.target.value)} className="input" autoComplete="off" placeholder="ws://127.0.0.1:8080/ws" />
      </label>

      <label style={{ display: 'block' }}>
        <span className="field-label">Auth token</span>
        <input type="password" value={token} onChange={(e) => setToken(e.target.value)} className="input" autoComplete="new-password" placeholder={config.hasToken ? '•••••••• configured — leave blank to keep' : 'Shared secret from sidecar.toml'} />
      </label>

      <label style={{ display: 'block', maxWidth: 140 }}>
        <span className="field-label">Protocol</span>
        <input type="number" value={protocol} onChange={(e) => setProtocol(e.target.value)} className="input" min={1} max={99} />
      </label>

      <div style={{ display: 'flex', gap: 10, alignItems: 'center', marginTop: 4 }}>
        <button onClick={save} disabled={busy} className="btn btn-primary btn-sq">{busy ? 'Saving…' : 'Save changes'}</button>
        {msg && <span className="sans" style={{ color: '#7fd0a4', fontSize: '0.85rem' }}>{msg}</span>}
        {saveError && <span className="sans" style={{ color: '#d98b84', fontSize: '0.85rem' }}>{saveError}</span>}
      </div>

      <TownCrier />

      <AdminLiveFeed />
    </section>
  )
}
