import { useCallback, useState } from 'react'
import { Loading, ErrorState } from '../../../components/PageState.jsx'
import { useAsync } from '../../../lib/useAsync.js'
import { ago, dateTime } from '../../../lib/format.js'
import { api } from '../../../api/client.js'
import { useSite } from '../../../contexts/SiteContext.jsx'
import { useAuth } from '../../../contexts/AuthContext.jsx'

export default function Dashboard() {
  const { refresh: refreshSite } = useSite()
  const { user } = useAuth()
  // PUT /admin/site-mode is adminOnly. The dashboard itself is staff-wide, so the
  // toggle needs its own gate — same rule the sidebar follows (AdminLayout: never
  // show a non-admin a control that would 403).
  const isAdmin = user?.role === 'admin'
  const [tick, setTick] = useState(0)
  const reload = useCallback(() => setTick((t) => t + 1), [])

  const { loading, error, data } = useAsync(
    () => Promise.all([api.admin.dashboard(), api.admin.listPosts(), api.admin.listWiki()]),
    [tick],
  )
  const [busy, setBusy] = useState(false)
  const [modeError, setModeError] = useState('')

  if (loading) return <Loading />
  if (error) return <ErrorState message="Could not load the dashboard." />

  const [dash, posts, wiki] = data
  const mode = dash.site_mode || 'live'
  const isLive = mode === 'live'
  const modeDot = isLive ? 'var(--mode-live)' : 'var(--mode-maint)'
  const published = posts.filter((p) => p.published).length

  const stats = [
    { value: published, label: 'Published posts' },
    { value: posts.length - published, label: 'Drafts' },
    { value: wiki.length, label: 'Wiki pages' },
    { value: dash.counts?.users ?? 0, label: 'Users' },
  ]

  // The rejection was previously unhandled: a refused toggle surfaced only as an
  // unhandled promise rejection in the console while the button silently reverted.
  async function toggle() {
    setBusy(true)
    setModeError('')
    try {
      await api.admin.setSiteMode(isLive ? 'maintenance' : 'live')
      await refreshSite()
      reload()
    } catch (err) {
      setModeError(
        err.status === 403
          ? 'Only an administrator can change the site mode.'
          : 'Could not change the site mode. Try again.',
      )
    } finally {
      setBusy(false)
    }
  }

  const changed = dash.last_change || {}

  let modeLabel = isLive ? 'Switch to Maintenance' : 'Switch to Live'
  if (busy) modeLabel = 'Saving…'

  return (
    <section>
      <div
        style={{
          display: 'flex',
          flexWrap: 'wrap',
          alignItems: 'center',
          justifyContent: 'space-between',
          gap: 18,
          padding: 24,
          border: '1px solid var(--line)',
          borderRadius: 12,
          background: 'var(--panel-grad)',
          marginBottom: 24,
        }}
      >
        <div>
          <div className="card-kicker" style={{ marginBottom: 8 }}>
            Site mode
          </div>
          <div style={{ display: 'flex', alignItems: 'center', gap: 10 }}>
            <span style={{ width: 11, height: 11, borderRadius: '50%', background: modeDot, boxShadow: `0 0 10px ${modeDot}` }} />
            <span className="display" style={{ fontSize: '1.5rem', color: 'var(--head)', textTransform: 'capitalize' }}>
              {mode}
            </span>
          </div>
          <div className="sans dim" style={{ fontSize: '0.8rem', marginTop: 6 }}>
            {changed.by ? `Changed by ${changed.by}` : 'No changes recorded'}
            {changed.at ? ` · ${dateTime(changed.at)}` : ''}
          </div>
          {modeError && (
            <div className="sans" style={{ fontSize: '0.8rem', marginTop: 8, color: 'var(--danger, #d98b8b)' }}>
              {modeError}
            </div>
          )}
        </div>
        {isAdmin && (
          <button
            onClick={toggle}
            disabled={busy}
            className="sans"
            style={{ border: '1px solid var(--accent)', borderRadius: 999, padding: '11px 24px', background: 'rgba(127,153,189,0.14)', color: '#d8e2ef', fontWeight: 600, fontSize: '0.9rem', cursor: 'pointer' }}
          >
            {modeLabel}
          </button>
        )}
      </div>

      <div className="grid-4" style={{ gap: 14, marginBottom: 28 }}>
        {stats.map((s) => (
          <div key={s.label} style={{ padding: 20, border: '1px solid var(--line)', borderRadius: 12, background: 'var(--panel-grad)' }}>
            <div className="display" style={{ fontSize: '2rem', color: 'var(--head)', lineHeight: 1 }}>
              {s.value}
            </div>
            <div className="card-kicker" style={{ marginTop: 8, marginBottom: 0 }}>
              {s.label}
            </div>
          </div>
        ))}
      </div>

      <h2 className="display" style={{ margin: '0 0 12px', fontSize: '1.25rem', color: 'var(--head)' }}>
        Recent activity
      </h2>
      <div className="panel-flat">
        {(dash.recent_activity || []).length === 0 && (
          <div className="adm-td" style={{ borderBottom: 'none' }}>No activity yet.</div>
        )}
        {(dash.recent_activity || []).map((a) => (
          <div
            key={a.id}
            className="sans"
            style={{ display: 'flex', gap: 14, alignItems: 'center', padding: '13px 18px', borderBottom: '1px solid var(--line-soft)', fontSize: '0.86rem' }}
          >
            <span style={{ flex: 'none', color: 'var(--accent)', fontSize: '0.66rem', fontWeight: 700, letterSpacing: '0.08em', textTransform: 'uppercase', width: 110, fontFamily: 'ui-monospace,Menlo,monospace' }}>
              {a.action}
            </span>
            <span style={{ flex: 1, color: 'var(--text)' }}>{formatDetail(a)}</span>
            <span className="dim" style={{ flex: 'none' }}>{ago(a.created_at)}</span>
          </div>
        ))}
      </div>
    </section>
  )
}

// Render the JSON `detail` column in a human-ish way.
export function formatDetail(a) {
  if (!a.detail) return a.username ? `by ${a.username}` : '—'
  try {
    const obj = JSON.parse(a.detail)
    return Object.entries(obj)
      .map(([k, v]) => `${k}: ${v}`)
      .join(', ')
  } catch {
    return a.detail
  }
}
