import { useEffect, useState } from 'react'
import { Link, useNavigate, useParams } from 'react-router-dom'
import { formatINR } from '@sims/domain'
import {
  Badge, Button, ConfirmDialog, DataTable, EmptyState, ErrorState, Notice, RecordHeader, Skeleton,
  Toolbar, useToast,
} from '@sims/ui'
import {
  createShare, documentAction, downloadDocumentPdf, fetchPdfBlob, getClient, getDocumentFull,
  revokeShare, type Client, type Doc, type Share,
} from '../api'
import { SendDialog } from '../components/SendDialog'
import { DOC_TONE, useData } from './Clients'
import { PaymentForm } from './client-forms'

/** One pending confirmation — replaces the browser confirm() popup with the styled dialog. */
interface Confirm { title: string; body: string; label: string; tone?: 'danger'; run: () => void }

const TITLE: Record<Doc['docType'], string> = {
  QUOTATION: 'Quotation', PROFORMA: 'Proforma Invoice', INVOICE: 'Tax Invoice',
  RECEIPT: 'Receipt', CREDIT_NOTE: 'Credit Note',
}

/** Friendly text for the send-route's 409s (the spec's token-death banner). */
function friendly(msg: string): string {
  if (msg === 'gmail-token-dead') return 'Gmail token dead — reconnect by running gmail-connect on the server, then retry.'
  if (msg === 'gmail-not-connected') return 'Gmail is not connected — run gmail-connect on the server first.'
  return msg
}

/** Document page: PDF preview, state-driven action bar, event timeline + email log. */
export function DocumentView() {
  const id = useParams()['id'] ?? ''
  const nav = useNavigate()
  const full = useData(() => getDocumentFull(id), [id])
  const doc = full.data?.document
  const client = useData<Client | undefined>(
    () => (doc !== undefined ? getClient(doc.clientId) : Promise.resolve(undefined)),
    [doc?.clientId],
  )
  const toast = useToast()
  const [busy, setBusy] = useState<string | undefined>()
  const [paying, setPaying] = useState(false)
  const [sending, setSending] = useState(false)
  const [confirm, setConfirm] = useState<Confirm | undefined>()

  // PDF preview via blob URL — an <iframe src> can't carry the bearer header.
  const [pdfUrl, setPdfUrl] = useState<string | undefined>()
  const [pdfErr, setPdfErr] = useState<string | undefined>()
  useEffect(() => {
    if (doc === undefined) return
    let alive = true
    let url: string | undefined
    setPdfErr(undefined)
    fetchPdfBlob(id)
      .then((blob) => {
        if (!alive) return
        url = URL.createObjectURL(blob)
        setPdfUrl(url)
      })
      .catch((e: Error) => { if (alive) setPdfErr(e.message) })
    return () => {
      alive = false
      if (url !== undefined) URL.revokeObjectURL(url)
    }
  }, [id, doc?.docNo, doc?.status])

  if (full.error !== undefined) return <div className="wf-page"><ErrorState message={full.error} onRetry={full.reload} /></div>
  if (doc === undefined || full.data === undefined) return <div className="wf-page"><Skeleton rows={6} /></div>

  /** POST an action; convert/credit-note navigate to the new document; toasts report the outcome. */
  const act = (action: string, body?: Record<string, unknown>, opts: { navigate?: boolean; ok?: string } = {}) => {
    setBusy(action)
    documentAction(id, action, body)
      .then((out) => {
        if (opts.ok !== undefined) toast.ok(opts.ok)
        if (opts.navigate === true && out.id !== id) nav(`/documents/${out.id}`)
        else full.reload()
      })
      .catch((e: Error) => toast.err(friendly(e.message)))
      .finally(() => setBusy(undefined))
  }

  const issued = doc.docNo !== null
  const cancelled = doc.status === 'cancelled'
  const settling = doc.status === 'part_paid' || doc.status === 'paid'

  return (
    <div className="wf-page">
      <div className="wf-crumbs">
        <Link to="/clients">Clients</Link><span>/</span>
        {client.data !== undefined ? <Link to={`/clients/${doc.clientId}`}>{client.data.name}</Link> : <span>…</span>}
        <span>/</span><span className="mono">{doc.docNo ?? 'draft'}</span>
      </div>
      <RecordHeader
        name={TITLE[doc.docType]}
        avatarSeed={client.data?.name ?? doc.clientId}
        status={<Badge tone={DOC_TONE[doc.status]}>{doc.status}</Badge>}
        meta={
          <>
            <span className="mono">{doc.docNo ?? '(draft)'}</span>
            <span>· {doc.fy}</span>
            <span>· {doc.docDate}</span>
            <span>· Payable <strong className="num">{formatINR(doc.payablePaise)}</strong></span>
          </>
        }
      />

      <Toolbar>
        {!issued && !cancelled && (
          <Button tone="primary" onClick={() => act('issue', undefined, { ok: 'Number issued' })}>
            {busy === 'issue' ? 'Issuing…' : 'Issue number'}
          </Button>
        )}
        {issued && !cancelled && (
          <Button tone="primary" onClick={() => setSending(true)}>
            {busy === 'send' ? 'Sending…' : 'Send by email'}
          </Button>
        )}
        {issued && doc.status === 'draft' && (
          <Button onClick={() => act('status', { status: 'sent' }, { ok: 'Marked sent' })}>Mark sent (manual)</Button>
        )}
        {doc.docType === 'QUOTATION' && doc.status === 'sent' && (
          <>
            <Button onClick={() => act('status', { status: 'accepted' }, { ok: 'Marked accepted' })}>Mark accepted</Button>
            <Button onClick={() => act('status', { status: 'lost' }, { ok: 'Marked lost' })}>Mark lost</Button>
          </>
        )}
        {doc.docType === 'QUOTATION' && !cancelled && (
          <Button onClick={() => act('convert', { to: 'PROFORMA' }, { navigate: true, ok: 'Converted to proforma' })}>
            Convert to Proforma
          </Button>
        )}
        {(doc.docType === 'QUOTATION' || doc.docType === 'PROFORMA') && !cancelled && (
          <Button onClick={() => act('convert', { to: 'INVOICE' }, { navigate: true, ok: 'Converted to invoice' })}>
            Convert to Invoice
          </Button>
        )}
        {doc.docType === 'PROFORMA' && !cancelled && doc.status !== 'invoiced' && (
          <>
            {/* Spec §8 one-click: convert → issue → email, in one action. */}
            <Button
              tone="primary"
              onClick={() => setConfirm({
                title: 'Convert to invoice & send',
                body: 'Convert to invoice, assign its number and email it to the client?',
                label: 'Convert & send',
                run: () => act('convert-and-send', {}, { navigate: true, ok: 'Converted to invoice & sent' }),
              })}
            >
              {busy === 'convert-and-send' ? 'Converting…' : 'Convert to invoice & send'}
            </Button>
            {/* Spec §8 cancel & recreate: retire this proforma, reopen its lines as a fresh draft. */}
            <Button
              onClick={() => setConfirm({
                title: 'Supersede & recreate',
                body: 'Supersede this proforma? It is cancelled (number stays consumed) and a fresh draft opens with the same lines.',
                label: 'Supersede',
                tone: 'danger',
                run: () => act('supersede', {}, { navigate: true, ok: 'Superseded' }),
              })}
            >
              {busy === 'supersede' ? 'Superseding…' : 'Supersede & recreate'}
            </Button>
          </>
        )}
        {doc.docType === 'INVOICE' && issued && !cancelled && doc.status !== 'paid' && (
          <Button tone="primary" onClick={() => setPaying((v) => !v)}>Record payment</Button>
        )}
        {issued && !cancelled && !settling && (
          <Button
            tone="danger"
            onClick={() => setConfirm({
              title: 'Cancel document',
              body: `Cancel ${doc.docNo}? The number stays consumed.`,
              label: 'Cancel document',
              tone: 'danger',
              run: () => act('cancel', undefined, { ok: 'Cancelled' }),
            })}
          >
            Cancel
          </Button>
        )}
        {doc.docType === 'INVOICE' && issued && !cancelled && (
          <Button
            onClick={() => setConfirm({
              title: 'Create credit note',
              body: `Create a full-value credit note against ${doc.docNo}?`,
              label: 'Create credit note',
              run: () => act('credit-note', {}, { navigate: true, ok: 'Credit note created' }),
            })}
          >
            Credit note
          </Button>
        )}
      </Toolbar>

      <SendDialog
        open={sending}
        onClose={() => setSending(false)}
        doc={doc}
        client={client.data}
        onSend={(to, subject) => act('send', { to, subject }, { ok: `Sent to ${to}` })}
      />

      <ShareActions doc={doc} client={client.data} shares={full.data.shares} onChange={full.reload} />

      {paying && (
        <PaymentForm
          clientId={doc.clientId}
          onDone={() => { setPaying(false); full.reload() }}
        />
      )}

      {pdfErr !== undefined && <Notice tone="warn">PDF preview unavailable: {pdfErr}</Notice>}
      {pdfUrl !== undefined && (
        <iframe
          src={pdfUrl} title="Document PDF"
          style={{ width: '100%', height: 560, border: '1px solid var(--border)', borderRadius: 8, background: '#fff' }}
        />
      )}

      <div className="dash-grid">
        <section>
          <h3 style={{ marginTop: 20 }}>Timeline</h3>
          {full.data.events.length === 0 ? <EmptyState>No events.</EmptyState> : (
            <DataTable
              columns={[
                { key: 'at', label: 'When' }, { key: 'kind', label: 'Event' }, { key: 'meta', label: 'Detail' },
              ]}
              rows={full.data.events.map((ev) => ({
                at: new Date(ev.atWall).toLocaleString(),
                kind: <Badge tone="accent">{ev.kind}</Badge>,
                meta: Object.keys(ev.meta).length > 0 ? JSON.stringify(ev.meta) : '—',
              }))}
            />
          )}
        </section>

        <section>
          <h3 style={{ marginTop: 20 }}>Email log</h3>
          {full.data.emails.length === 0 ? <EmptyState>Not emailed yet.</EmptyState> : (
            <DataTable
              columns={[
                { key: 'at', label: 'When' }, { key: 'to', label: 'To' },
                { key: 'subject', label: 'Subject' }, { key: 'status', label: 'Status' },
                { key: 'error', label: 'Error' },
              ]}
              rows={full.data.emails.map((e) => ({
                at: new Date(e.at_wall).toLocaleString(),
                to: e.to_addr, subject: e.subject,
                status: <Badge tone={e.status === 'sent' ? 'ok' : 'err'}>{e.status}</Badge>,
                error: e.error ?? '—',
              }))}
            />
          )}
        </section>
      </div>

      {confirm !== undefined && (
        <ConfirmDialog
          open
          onClose={() => setConfirm(undefined)}
          onConfirm={confirm.run}
          title={confirm.title}
          body={confirm.body}
          confirmLabel={confirm.label}
          {...(confirm.tone !== undefined ? { tone: confirm.tone } : {})}
        />
      )}
    </div>
  )
}

/** True while a share is still usable — not revoked and not past its expiry. */
function isLive(s: Share): boolean {
  return !s.revoked && (s.expiresAt === null || s.expiresAt > new Date().toISOString())
}

/**
 * Share & download action group: download the PDF (attachment), mint/copy a public
 * `/share/<token>` view link, list the live links with one-click revoke, and hand the
 * link to WhatsApp click-to-chat or the native share sheet (falling back to Download
 * where `navigator.share` is unavailable). The public link is same-origin — served by
 * the hq server (in production it also serves this SPA; in dev vite proxies `/share`).
 */
function ShareActions(props: {
  doc: Doc; client: Client | undefined; shares: Share[]; onChange: () => void
}) {
  const { doc, client, shares, onChange } = props
  const [busy, setBusy] = useState<string | undefined>()
  const [err, setErr] = useState<string | undefined>()
  const [copied, setCopied] = useState<string | undefined>()
  const [confirm, setConfirm] = useState<{ id: string } | undefined>()

  const shareUrl = (token: string): string => `${location.origin}/share/${token}`
  const live = shares.filter(isLive)
  const primary = live[0] // server lists newest-first (uuidv7 id DESC)

  const run = (key: string, p: Promise<unknown>) => {
    setErr(undefined)
    setBusy(key)
    p.then(() => onChange()).catch((e: Error) => setErr(e.message)).finally(() => setBusy(undefined))
  }

  const onDownload = () => {
    setErr(undefined)
    setBusy('download')
    downloadDocumentPdf(doc.id, doc.docNo)
      .catch((e: Error) => setErr(e.message))
      .finally(() => setBusy(undefined))
  }

  const onCopy = (url: string) => {
    void navigator.clipboard?.writeText(url).then(
      () => { setCopied(url); setTimeout(() => setCopied(undefined), 1800) },
      () => setErr('Could not copy — select the link and copy it manually.'),
    )
  }

  // Greeting + doc type/no + total + the view link — the message that rides the share.
  const message = (url: string): string => {
    const who = client?.name !== undefined && client.name !== '' ? `Dear ${client.name},` : 'Hello,'
    const no = doc.docNo ?? '(draft)'
    return `${who}\n\nPlease find your ${TITLE[doc.docType]} ${no} for ${formatINR(doc.payablePaise)}.\n`
      + `\nView it here: ${url}`
  }

  const onWhatsApp = (url: string) => {
    const digits = (client?.contacts.find((c) => c.phone !== undefined && c.phone !== '')?.phone ?? '')
      .replace(/\D/g, '')
    // With digits → chat opens on that number; without → WhatsApp asks which contact.
    open(`https://wa.me/${digits}?text=${encodeURIComponent(message(url))}`, '_blank', 'noopener')
  }

  const onNativeShare = (url: string) => {
    const nav = navigator as Navigator & { share?: (d: ShareData) => Promise<void> }
    if (typeof nav.share === 'function') {
      void nav.share({ title: `${TITLE[doc.docType]} ${doc.docNo ?? ''}`.trim(), text: message(url), url })
        .catch(() => { /* user dismissed the sheet — nothing to do */ })
    } else {
      onDownload() // desktop / unsupported → save the PDF instead
    }
  }

  return (
    <section style={{ marginTop: 14 }}>
      <h3 style={{ margin: '0 0 8px' }}>Share &amp; download</h3>
      <Toolbar>
        <Button onClick={onDownload}>{busy === 'download' ? 'Preparing…' : 'Download PDF'}</Button>
        {primary === undefined ? (
          <Button tone="primary" onClick={() => run('create', createShare(doc.id))}>
            {busy === 'create' ? 'Creating…' : 'Create share link'}
          </Button>
        ) : (
          <>
            <Button onClick={() => onCopy(shareUrl(primary.token))}>
              {copied === shareUrl(primary.token) ? 'Copied ✓' : 'Copy link'}
            </Button>
            <Button onClick={() => onWhatsApp(shareUrl(primary.token))}>WhatsApp</Button>
            <Button onClick={() => onNativeShare(shareUrl(primary.token))}>Share…</Button>
            <Button onClick={() => run('create', createShare(doc.id))}>
              {busy === 'create' ? 'Creating…' : 'New link'}
            </Button>
          </>
        )}
      </Toolbar>
      {err !== undefined && <Notice tone="err">{err}</Notice>}
      {live.length === 0 ? (
        <p style={{ color: 'var(--text-dim)', fontSize: 13, margin: '2px 0 0' }}>
          No active link yet — create one to share this {TITLE[doc.docType].toLowerCase()} by link, WhatsApp, or the share sheet.
        </p>
      ) : (
        <div style={{ marginTop: 8, display: 'flex', flexDirection: 'column', gap: 6 }}>
          {live.map((s) => (
            <div
              key={s.id}
              style={{
                display: 'flex', alignItems: 'center', gap: 10, flexWrap: 'wrap',
                border: '1px solid var(--border)', borderRadius: 8, padding: '8px 10px',
              }}
            >
              <code style={{ fontFamily: 'var(--mono)', fontSize: 12.5, wordBreak: 'break-all', flex: 1, minWidth: 220 }}>
                {shareUrl(s.token)}
              </code>
              <span style={{ fontSize: 12, color: 'var(--text-dim)', whiteSpace: 'nowrap' }}>
                {s.expiresAt === null ? 'No expiry' : `Expires ${new Date(s.expiresAt).toLocaleDateString()}`}
              </span>
              <Button onClick={() => onCopy(shareUrl(s.token))}>
                {copied === shareUrl(s.token) ? 'Copied ✓' : 'Copy'}
              </Button>
              <Button tone="danger" onClick={() => setConfirm({ id: s.id })}>
                {busy === `revoke:${s.id}` ? 'Revoking…' : 'Revoke'}
              </Button>
            </div>
          ))}
        </div>
      )}
      {confirm !== undefined && (
        <ConfirmDialog
          open
          onClose={() => setConfirm(undefined)}
          onConfirm={() => run(`revoke:${confirm.id}`, revokeShare(doc.id, confirm.id))}
          title="Revoke link"
          body="Revoke this share link? Anyone with it will lose access immediately."
          confirmLabel="Revoke"
          tone="danger"
        />
      )}
    </section>
  )
}
