/**
 * View builders. Every dynamic value (host subdomain/status/dates, pairing code, command) is placed
 * with `textContent` via the `el` helper — NEVER innerHTML — so host data (treated as untrusted for
 * XSS even though it comes from an authenticated CP) can't inject markup. The QR is an <img> whose
 * src is a data: URL our own backend generated with the `qrcode` lib.
 */
import { el, clear, type Child } from './dom.js'
import type { HostView, PairingArtifacts } from './api.js'

export interface DashboardHandlers {
  onNewCode: () => void
  onRevoke: (host: HostView) => void
  onLogout: () => void
  onRefresh: () => void
}

/** Login card with a password field; `onSubmit(password)` fires on submit. */
export function renderLogin(onSubmit: (password: string) => void): HTMLElement {
  const input = el('input', { type: 'password', name: 'password', placeholder: 'Panel password', autocomplete: 'current-password' })
  const error = el('p', { class: 'error', role: 'alert' })
  const form = el(
    'form',
    {
      class: 'card login',
      onSubmit: (e) => {
        e.preventDefault()
        error.textContent = ''
        onSubmit(input.value)
      },
    },
    [
      el('h1', { text: 'Tunnel Control Panel' }),
      el('label', { text: 'Password', class: 'field-label' }),
      input,
      el('button', { type: 'submit', class: 'btn primary', text: 'Sign in' }),
      error,
    ],
  )
  const wrap = el('div', { class: 'centered' }, [form])
  // Expose the error node so the app can show login failures.
  ;(wrap as HTMLElement & { errorNode?: HTMLElement }).errorNode = error
  queueMicrotask(() => input.focus())
  return wrap
}

function statusPill(status: string): HTMLElement {
  const known = ['online', 'offline', 'revoked', 'suspended', 'active'].includes(status)
  return el('span', { class: `pill pill-${known ? status : 'unknown'}`, text: status })
}

function fmtDate(value: string | undefined | null): string {
  if (value === undefined || value === null || value === '') return '—'
  const t = Date.parse(value)
  return Number.isNaN(t) ? value : new Date(t).toLocaleString()
}

function hostRow(host: HostView, handlers: DashboardHandlers): HTMLElement {
  const revoke = el('button', {
    class: 'btn danger small',
    text: 'Revoke',
    title: `Revoke ${host.subdomain}`,
    onClick: () => handlers.onRevoke(host),
  })
  return el('tr', {}, [
    el('td', { text: host.subdomain, class: 'mono' }),
    el('td', {}, [statusPill(host.status)]),
    el('td', { text: fmtDate(host.notAfter), class: 'muted' }),
    el('td', { text: fmtDate(host.lastSeen), class: 'muted' }),
    el('td', {}, [revoke]),
  ])
}

function hostsTable(hosts: readonly HostView[], handlers: DashboardHandlers): HTMLElement {
  if (hosts.length === 0) {
    return el('div', { class: 'empty', text: 'No hosts enrolled yet. Create a pairing code to add one.' })
  }
  const head = el('thead', {}, [
    el('tr', {}, [
      el('th', { text: 'Subdomain' }),
      el('th', { text: 'Status' }),
      el('th', { text: 'Cert expiry' }),
      el('th', { text: 'Last seen' }),
      el('th', { text: '' }),
    ]),
  ])
  const body = el('tbody', {}, hosts.map((h) => hostRow(h, handlers)))
  return el('div', { class: 'table-scroll' }, [el('table', { class: 'hosts' }, [head, body])])
}

/** Full dashboard: header (refresh / new-code / logout) + hosts table. */
export function renderDashboard(hosts: readonly HostView[], handlers: DashboardHandlers): HTMLElement {
  const header = el('header', { class: 'topbar' }, [
    el('h1', { text: 'Tunnel Control Panel' }),
    el('div', { class: 'actions' }, [
      el('button', { class: 'btn', text: 'Refresh', onClick: () => handlers.onRefresh() }),
      el('button', { class: 'btn primary', text: 'New pairing code', onClick: () => handlers.onNewCode() }),
      el('button', { class: 'btn ghost', text: 'Log out', onClick: () => handlers.onLogout() }),
    ]),
  ])
  return el('div', { class: 'dashboard' }, [
    header,
    el('section', { class: 'card' }, [el('h2', { text: 'Hosts' }), hostsTable(hosts, handlers)]),
  ])
}

/** Generic modal overlay; returns { overlay, close }. Clicking the backdrop or ✕ closes it. */
function modal(title: string, children: Child[]): { overlay: HTMLElement; close: () => void } {
  const close = (): void => overlay.remove()
  const dialog = el('div', { class: 'modal', role: 'dialog' }, [
    el('div', { class: 'modal-head' }, [el('h2', { text: title }), el('button', { class: 'icon-btn', text: '✕', ariaLabel: 'Close', onClick: close })]),
    el('div', { class: 'modal-body' }, children),
  ])
  const overlay = el('div', { class: 'overlay', onClick: (e) => { if (e.target === overlay) close() } }, [dialog])
  return { overlay, close }
}

/** Pairing-code modal: the code, its QR, the copyable command, and the expiry. */
export function pairingModal(artifacts: PairingArtifacts): HTMLElement {
  const command = el('code', { class: 'command', text: artifacts.pairCommand })
  const copyBtn = el('button', {
    class: 'btn small',
    text: 'Copy command',
    onClick: () => {
      void navigator.clipboard?.writeText(artifacts.pairCommand).then(
        () => { copyBtn.textContent = 'Copied!' },
        () => { copyBtn.textContent = 'Copy failed' },
      )
    },
  })
  const { overlay } = modal('Pairing code', [
    el('p', { class: 'muted', text: 'Run this on the new host, or scan the QR from the phone client. Single-use.' }),
    el('div', { class: 'code-big mono', text: artifacts.code }),
    el('img', { class: 'qr', src: artifacts.qrDataUrl, alt: 'Pairing code QR' }),
    el('div', { class: 'command-row' }, [command, copyBtn]),
    el('p', { class: 'muted small', text: `Expires: ${fmtDate(artifacts.expiresAt)}` }),
  ])
  return overlay
}

/** Confirm dialog → resolves true (confirmed) / false (cancelled). */
export function confirmDialog(message: string): Promise<boolean> {
  return new Promise((resolve) => {
    const { overlay, close } = modal('Please confirm', [
      el('p', { text: message }),
      el('div', { class: 'command-row end' }, [
        el('button', { class: 'btn ghost', text: 'Cancel', onClick: () => { close(); resolve(false) } }),
        el('button', { class: 'btn danger', text: 'Revoke', onClick: () => { close(); resolve(true) } }),
      ]),
    ])
    document.body.append(overlay)
  })
}

/** A transient toast for errors/success (textContent only). */
export function toast(root: HTMLElement, message: string, kind: 'error' | 'info' = 'info'): void {
  const t = el('div', { class: `toast toast-${kind}`, text: message, role: 'status' })
  root.append(t)
  setTimeout(() => t.remove(), 4000)
}

export { clear }
