import { useEffect, useMemo, useRef, useState } from 'react'
import { api } from './api'
import type { JobSnapshot } from './types'
import { duration, humanBytes, Notice, Progress, StateDot } from './ui'

export function Jobs({
  jobs, activeJob, setActiveJob, reload, reloadPackages,
}: {
  jobs: JobSnapshot[]
  activeJob: string
  setActiveJob: (id: string) => void
  reload: () => void
  reloadPackages: () => void
}) {
  const selected = activeJob || jobs[0]?.id || ''

  if (jobs.length === 0) {
    return <div className="empty">no migrations yet — select containers and start one</div>
  }

  return (
    <div style={{ display: 'flex', minHeight: 0, height: '100%' }}>
      <div className="joblist" style={{ width: 320, flex: '0 0 320px', overflow: 'auto' }}>
        {jobs.map((j) => (
          <div
            key={j.id}
            className={`jobcard${j.id === selected ? ' active' : ''}`}
            onClick={() => setActiveJob(j.id)}
          >
            <div className="row">
              <StateDot state={j.state} />
              <span className="spacer" />
              <span className="small faint">{j.kind === 'ssh' ? 'ssh' : 'package'}</span>
            </div>
            <div className="truncate" style={{ marginTop: 2 }}>{j.title}</div>
            <div className="small faint">
              {new Date(j.createdAt).toLocaleTimeString()} · {duration(j.startedAt, j.endedAt)}
              {j.dryRun && ' · dry run'}
            </div>
            <div style={{ marginTop: 6 }}>
              <Progress done={j.bytesDone} total={j.bytesTotal} state={j.state} />
            </div>
          </div>
        ))}
      </div>
      <div style={{ flex: 1, minWidth: 0, overflow: 'auto', borderLeft: '1px solid var(--border)' }}>
        {selected && <JobDetail id={selected} reload={reload} reloadPackages={reloadPackages} />}
      </div>
    </div>
  )
}

function JobDetail({
  id, reload, reloadPackages,
}: {
  id: string
  reload: () => void
  reloadPackages: () => void
}) {
  const [job, setJob] = useState<JobSnapshot | null>(null)
  const [showCmds, setShowCmds] = useState(true)
  const logRef = useRef<HTMLDivElement>(null)
  const stick = useRef(true)

  // Each job detail holds one server-sent events stream, which pushes a full
  // snapshot whenever anything changes.
  useEffect(() => {
    setJob(null)
    let closed = false
    api.job(id).then((j) => !closed && setJob(j)).catch(() => undefined)

    const es = api.jobEvents(id)
    es.onmessage = (ev) => {
      try {
        setJob(JSON.parse(ev.data) as JobSnapshot)
      } catch { /* a truncated frame is replaced by the next one */ }
    }
    es.addEventListener('done', () => {
      es.close()
      reload()
      reloadPackages()
    })
    es.onerror = () => es.close()
    return () => {
      closed = true
      es.close()
    }
  }, [id, reload, reloadPackages])

  const lines = useMemo(
    () => (job?.log ?? []).filter((l) => showCmds || l.level !== 'cmd'),
    [job, showCmds],
  )

  useEffect(() => {
    const el = logRef.current
    if (el && stick.current) el.scrollTop = el.scrollHeight
  }, [lines])

  if (!job) return <div className="empty">loading…</div>

  const running = job.state === 'running' || job.state === 'pending'

  return (
    <div style={{ padding: 16, display: 'flex', flexDirection: 'column', gap: 14 }}>
      <div className="row">
        <StateDot state={job.state} />
        <b>{job.title}</b>
        {job.dryRun && <span className="badge">dry run</span>}
        <span className="spacer" />
        <span className="small faint">
          {humanBytes(job.bytesDone)}
          {job.bytesTotal > 0 && <> of {humanBytes(job.bytesTotal)}</>} · {duration(job.startedAt, job.endedAt)}
        </span>
        {running ? (
          <button className="btn tiny danger" onClick={() => api.cancelJob(job.id).then(reload)}>cancel</button>
        ) : (
          <button className="btn tiny ghost" onClick={() => api.deleteJob(job.id).then(reload)}>remove</button>
        )}
      </div>

      <Progress done={job.bytesDone} total={job.bytesTotal} state={job.state} />

      {job.error && <Notice kind="err">{job.error}</Notice>}
      {job.state === 'succeeded' && job.artifact && (
        <Notice kind="ok">
          Package ready at <span className="mono">{job.artifact}</span> ({humanBytes(job.artifactBytes ?? 0)}).
          {' '}Open the Packages tab to download it.
        </Notice>
      )}

      {job.items.map((it) => (
        <div key={it.id} style={{ border: '1px solid var(--border)', borderRadius: 6, padding: '8px 10px' }}>
          <div className="row">
            <StateDot state={it.state} />
            <b>{it.name}</b>
            <span className="spacer" />
            <span className="small faint">{it.steps.filter((s) => s.state === 'succeeded').length}/{it.steps.length} steps</span>
          </div>
          {it.error && <div className="small" style={{ color: 'var(--err)' }}>{it.error}</div>}
          {(it.warnings ?? []).map((w, i) => (
            <div key={i} className="small" style={{ color: 'var(--warn)' }}>! {w}</div>
          ))}
          <div className="steps">
            {it.steps.map((s) => (
              <div key={s.id} className={`step ${s.state}`}>
                <StateDot state={s.state} label="" />
                <span className="label truncate" title={s.error || s.label}>{s.label}</span>
                <span>
                  {s.bytesTotal > 0 || s.bytesDone > 0 ? (
                    <Progress done={s.bytesDone} total={s.bytesTotal} state={s.state} />
                  ) : null}
                </span>
                <span className="faint nowrap" style={{ textAlign: 'right' }}>
                  {s.bytesDone > 0 ? humanBytes(s.bytesDone) : s.state === 'skipped' ? 'skipped' : ''}
                </span>
              </div>
            ))}
          </div>
        </div>
      ))}

      <div className="row">
        <h3 style={{ margin: 0, fontSize: 12 }}>log</h3>
        <span className="spacer" />
        <label className="check small">
          <input type="checkbox" checked={showCmds} onChange={(e) => setShowCmds(e.target.checked)} />
          <span>show commands</span>
        </label>
      </div>
      <div
        className="log"
        ref={logRef}
        onScroll={(e) => {
          const el = e.currentTarget
          stick.current = el.scrollHeight - el.scrollTop - el.clientHeight < 24
        }}
      >
        {lines.map((l) => (
          <div key={l.seq} className={`l-${l.level}`}>
            <span className="ts">{new Date(l.at).toLocaleTimeString()} </span>
            {l.message}
          </div>
        ))}
        {lines.length === 0 && <span className="faint">nothing logged yet</span>}
      </div>
    </div>
  )
}
