import { useEffect, useMemo, useRef, useState } from 'react'
import { formatINR } from '@sims/domain'
import { parseEntry } from '@sims/scanning'
import { resolveTaxClass, type TaxClassRow } from '@sims/billing-engine'
import type { LabelInput } from '@sims/printing'
import { Badge, Button, DataTable, EmptyState, Field, Notice, PageHeader, Toolbar } from '@sims/ui'
import { getBootstrapPublic, getCostHistory, getItems, getLastCosts, getParties, getPurchases, getStock, postPurchase } from '../api'
import { openLabelSheet } from './labels'

/** Crash-proof draft (spec P1-5): a 180-line invoice must survive a browser crash. */
const DRAFT_KEY = 'pe.draft'
interface Draft {
  supplier?: { id: string; name: string }
  invoiceNo: string
  invoiceDate: string
  lines: PLine[]
}

/**
 * Purchase Entry — the second crown-jewel screen (09-UX §3): a purchaser types
 * a 50–200 line supplier invoice as fast as the paper can be read.
 * Flow per line: scan/type item → qty → cost (last-cost prefilled) → Enter
 * returns to the scan box. Cost changes prompt a price revision inline.
 */

interface ItemLite {
  id: string; code: string; name: string; taxClassCode: string
  salePricePaise: number; mrpPaise?: number
  unit: { code: string; decimals: number }; barcodes: string[]
}

interface PLine {
  itemId: string; name: string; unitCode: string
  qty: number; unitCostPaise: number; taxRateBp: number
  lastCostPaise?: number
  currentSalePaise: number; currentMrpPaise?: number
  newSalePricePaise?: number; newMrpPaise?: number
}

const inr = (p: number) => formatINR(p, { symbol: false })
const rs = (s: string) => Math.round(Number(s) * 100)

export function PurchaseEntryPage() {
  const today = new Date().toISOString().slice(0, 10)
  const [items, setItems] = useState<ItemLite[]>([])
  const [taxClasses, setTaxClasses] = useState<TaxClassRow[]>([])
  const [suppliers, setSuppliers] = useState<{ id: string; name: string }[]>([])
  const [supplier, setSupplier] = useState<{ id: string; name: string } | undefined>()
  const [supplierQuery, setSupplierQuery] = useState('')
  const [lastCost, setLastCost] = useState<Record<string, { last?: number; supplierLast?: number }>>({})
  const [invoiceNo, setInvoiceNo] = useState('')
  const [invoiceDate, setInvoiceDate] = useState(today)
  const [lines, setLines] = useState<PLine[]>([])
  const [entry, setEntry] = useState('')
  const [printedTotal, setPrintedTotal] = useState('')
  const [notice, setNotice] = useState<{ tone: 'ok' | 'warn' | 'err'; text: string } | undefined>()
  const [busy, setBusy] = useState(false)
  /** Filled on post so the receiving clerk can print labels for what just came in. */
  const [labelQueue, setLabelQueue] = useState<LabelInput[] | undefined>()
  /** Which cell owns focus: the scan box, or qty/cost of a line. */
  const [focusCell, setFocusCell] = useState<{ line: number; cell: 'qty' | 'cost' } | undefined>()

  const entryRef = useRef<HTMLInputElement>(null)
  const cellRef = useRef<HTMLInputElement>(null)

  const [stockMap, setStockMap] = useState<Map<string, number>>(new Map())
  const [draft, setDraft] = useState<Draft | undefined>()
  const [costHist, setCostHist] = useState<{ cost: number; date: string; supplier: string }[] | undefined>()

  useEffect(() => {
    Promise.all([getItems(), getParties('supplier'), getBootstrapPublic(), getStock()])
      .then(([its, sups, boot, stock]) => {
        setItems(its as unknown as ItemLite[])
        setSuppliers(sups.map((s) => ({ id: String(s['id']), name: String(s['name']) })))
        setTaxClasses(boot.taxClasses)
        setStockMap(new Map(stock.map((s) => [String(s['code']), Number(s['on_hand'])])))
      })
      .catch((e: Error) => setNotice({ tone: 'err', text: e.message }))
    // resume a crashed entry?
    try {
      const raw = localStorage.getItem(DRAFT_KEY)
      if (raw !== null) {
        const d = JSON.parse(raw) as Draft
        if (d.lines.length > 0) setDraft(d)
      }
    } catch { /* corrupted draft — ignore */ }
  }, [])

  // journal every change; cleared on post
  useEffect(() => {
    if (lines.length > 0) {
      localStorage.setItem(DRAFT_KEY, JSON.stringify({ supplier, invoiceNo, invoiceDate, lines } satisfies Draft))
    }
  }, [lines, supplier, invoiceNo, invoiceDate])

  // right-rail glanceability (spec P1-1): cost history for the line being edited
  useEffect(() => {
    const line = focusCell !== undefined ? lines[focusCell.line] : undefined
    if (line === undefined) return setCostHist(undefined)
    getCostHistory(line.itemId, supplier?.id).then(setCostHist).catch(() => setCostHist(undefined))
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, [focusCell?.line])

  useEffect(() => {
    getLastCosts(supplier?.id).then(setLastCost).catch(() => undefined)
  }, [supplier?.id])

  useEffect(() => {
    if (focusCell !== undefined) cellRef.current?.select()
    else entryRef.current?.focus()
  }, [focusCell, lines.length])

  const say = (tone: 'ok' | 'warn' | 'err', text: string) => setNotice({ tone, text })

  const rateOf = (item: ItemLite): number => {
    try {
      const r = resolveTaxClass(taxClasses, item.taxClassCode, invoiceDate)
      return r.ratePctBp + r.cessPctBp
    } catch {
      return 0
    }
  }

  const addItem = (item: ItemLite, qty = 1) => {
    const memory = lastCost[item.id]
    const prefill = memory?.supplierLast ?? memory?.last ?? 0
    setLines((prev) => {
      setFocusCell({ line: prev.length, cell: 'qty' })
      return [...prev, {
        itemId: item.id, name: item.name, unitCode: item.unit.code,
        qty, unitCostPaise: prefill, taxRateBp: rateOf(item),
        ...(memory?.supplierLast !== undefined || memory?.last !== undefined
          ? { lastCostPaise: memory.supplierLast ?? memory.last }
          : {}),
        currentSalePaise: item.salePricePaise,
        ...(item.mrpPaise !== undefined ? { currentMrpPaise: item.mrpPaise } : {}),
      }]
    })
    setNotice(undefined)
  }

  const handleEntry = () => {
    const parsed = parseEntry(entry)
    setEntry('')
    const find = (code: string) =>
      items.find((i) => i.barcodes.includes(code)) ?? items.find((i) => i.code === code)
    switch (parsed.kind) {
      case 'empty': return
      case 'multiplier': {
        if (parsed.rest === '') return say('warn', 'Type the item after the quantity, e.g. 24*surf')
        const item = /^\d+$/.test(parsed.rest)
          ? find(parsed.rest)
          : items.find((i) => i.name.toLowerCase().includes(parsed.rest.toLowerCase()))
        return item !== undefined ? addItem(item, parsed.qty) : say('err', `No item for "${parsed.rest}" (E-1102)`)
      }
      case 'barcode':
      case 'code': {
        const item = find(parsed.code)
        return item !== undefined ? addItem(item) : say('err', `Unknown code ${parsed.code} (E-1102)`)
      }
      case 'search': {
        const q = parsed.text.toLowerCase()
        const item = items.filter((i) => i.name.toLowerCase().includes(q))
          .sort((a, b) => Number(b.name.toLowerCase().startsWith(q)) - Number(a.name.toLowerCase().startsWith(q)))[0]
        return item !== undefined ? addItem(item) : say('err', `No item matches "${parsed.text}" (E-1102)`)
      }
    }
  }

  const setLine = (i: number, patch: Partial<PLine>) =>
    setLines((prev) => prev.map((l, j) => (j === i ? { ...l, ...patch } : l)))

  const totals = useMemo(() => {
    let taxable = 0, tax = 0
    for (const l of lines) {
      const t = Math.round(l.qty * l.unitCostPaise)
      taxable += t
      tax += Math.round((t * l.taxRateBp) / 10_000)
    }
    return { taxable, tax, total: taxable + tax }
  }, [lines])

  const printedPaise = printedTotal === '' ? undefined : rs(printedTotal)
  const crossCheck = printedPaise === undefined ? 'none' : printedPaise === totals.total ? 'match' : 'mismatch'

  const post = () => {
    if (busy) return
    if (supplier === undefined) return say('warn', 'Pick the supplier first')
    if (invoiceNo.trim() === '') return say('warn', 'Enter the supplier invoice number')
    if (lines.length === 0) return say('warn', 'No lines yet — scan or type the first item')
    if (crossCheck === 'mismatch') {
      return say('warn', `Printed total ${formatINR(printedPaise!)} ≠ computed ${formatINR(totals.total)} — fix a line or clear the check field to post anyway`)
    }
    setBusy(true)
    postPurchase({
      storeId: 's1', supplierId: supplier.id, invoiceNo: invoiceNo.trim(), invoiceDate,
      businessDate: today,
      lines: lines.map((l) => ({
        itemId: l.itemId, name: l.name, qty: l.qty, unitCostPaise: l.unitCostPaise,
        taxRateBp: l.taxRateBp,
        ...(l.newSalePricePaise !== undefined ? { newSalePricePaise: l.newSalePricePaise } : {}),
        ...(l.newMrpPaise !== undefined ? { newMrpPaise: l.newMrpPaise } : {}),
      })),
      clientTotalPaise: totals.total,
    })
      .then((res) => {
        say('ok', `Purchase posted — ${lines.length} lines, ${formatINR(res.totalPaise)}. Stock is in; price updates applied.`)
        // Label queue (09-UX §3.2): received qty each, MRP/price effective after the post,
        // first barcode from the item master. The clerk prints on the spot.
        setLabelQueue(lines.map((l): LabelInput => {
          const item = items.find((i) => i.id === l.itemId)
          const mrp = l.newMrpPaise ?? l.currentMrpPaise ?? l.newSalePricePaise ?? l.currentSalePaise
          const barcode = item?.barcodes.find((b) => b !== '')
          return {
            name: l.name, mrpPaise: mrp, pricePaise: l.newSalePricePaise ?? l.currentSalePaise,
            ...(barcode !== undefined ? { code13: barcode } : {}),
          }
        }))
        setLines([]); setInvoiceNo(''); setPrintedTotal('')
        localStorage.removeItem(DRAFT_KEY)
        getLastCosts(supplier.id).then(setLastCost).catch(() => undefined)
      })
      .catch((err: Error) => say('err', `${err.message} — nothing was posted`))
      .finally(() => setBusy(false))
  }

  useEffect(() => {
    const onKey = (e: KeyboardEvent) => {
      if (e.key === 'F12' || (e.ctrlKey && e.key === 'Enter')) { e.preventDefault(); post() }
      if (e.key === 'Escape') { setFocusCell(undefined); setNotice(undefined) }
    }
    window.addEventListener('keydown', onKey)
    return () => window.removeEventListener('keydown', onKey)
  })

  const supplierMatches = supplierQuery === '' ? [] :
    suppliers.filter((s) => s.name.toLowerCase().includes(supplierQuery.toLowerCase())).slice(0, 6)

  return (
    <div className="wf-page">
      <PageHeader
        title="Purchase Entry" badge="LIVE"
        desc="Scan or type item → qty → cost → back to scan. Last cost prefills; cost changes offer a price revision; F12 posts."
        actions={<Button tone="primary" onClick={post} hotkey="F12">{busy ? 'Posting…' : 'Post purchase'}</Button>}
      />

      <Toolbar>
        <div style={{ position: 'relative', minWidth: 260 }}>
          {supplier === undefined ? (
            <>
              <input
                className="wf" placeholder="Supplier… (type to search)"
                value={supplierQuery} onChange={(e) => setSupplierQuery(e.target.value)}
                onKeyDown={(e) => {
                  if (e.key === 'Enter' && supplierMatches[0] !== undefined) {
                    setSupplier(supplierMatches[0]); setSupplierQuery('')
                  }
                }}
              />
              {supplierMatches.length > 0 && (
                <div style={{ position: 'absolute', top: '100%', left: 0, right: 0, zIndex: 10 }} className="wf-modal">
                  {supplierMatches.map((s) => (
                    <div key={s.id} style={{ padding: '6px 8px', cursor: 'pointer' }}
                      onClick={() => { setSupplier(s); setSupplierQuery('') }}>{s.name}</div>
                  ))}
                </div>
              )}
            </>
          ) : (
            <Badge tone="accent">{supplier.name} ✕</Badge>
          )}
          {supplier !== undefined && (
            <Button onClick={() => setSupplier(undefined)}>Change</Button>
          )}
        </div>
        <input className="wf" style={{ maxWidth: 160 }} placeholder="Invoice no" value={invoiceNo} onChange={(e) => setInvoiceNo(e.target.value)} />
        <input type="date" className="wf" style={{ maxWidth: 160 }} value={invoiceDate} onChange={(e) => setInvoiceDate(e.target.value)} />
      </Toolbar>

      <input
        ref={entryRef}
        className="wf scanbox" style={{ fontSize: 17, fontFamily: 'var(--mono)', marginBottom: 8 }}
        placeholder="Scan barcode · item code · name · 24*surf for quantity"
        value={entry}
        onChange={(e) => setEntry(e.target.value)}
        onKeyDown={(e) => { if (e.key === 'Enter') { e.preventDefault(); handleEntry() } }}
      />
      {notice !== undefined && <Notice tone={notice.tone}>{notice.text}</Notice>}

      {labelQueue !== undefined && (
        <Notice tone="ok">
          Print labels for {labelQueue.length} received item(s)?{' '}
          <Button tone="primary" onClick={() => { const err = openLabelSheet(labelQueue); if (err !== undefined) say('warn', err) }}>Print labels ↗</Button>{' '}
          <Button onClick={() => setLabelQueue(undefined)}>Dismiss</Button>
        </Notice>
      )}

      {draft !== undefined && (
        <Notice tone="warn">
          Unfinished entry found — <b>{draft.invoiceNo !== '' ? draft.invoiceNo : 'no invoice no'}</b> with {draft.lines.length} line(s).{' '}
          <Button tone="primary" onClick={() => {
            setLines(draft.lines)
            setInvoiceNo(draft.invoiceNo)
            setInvoiceDate(draft.invoiceDate)
            if (draft.supplier !== undefined) setSupplier(draft.supplier)
            setDraft(undefined)
          }}>Resume</Button>{' '}
          <Button onClick={() => { localStorage.removeItem(DRAFT_KEY); setDraft(undefined) }}>Discard</Button>
        </Notice>
      )}

      {focusCell !== undefined && lines[focusCell.line] !== undefined && (() => {
        const l = lines[focusCell.line]!
        const item = items.find((i) => i.id === l.itemId)
        const margin = l.unitCostPaise > 0 ? ((l.currentSalePaise - l.unitCostPaise) / l.unitCostPaise) * 100 : 0
        return (
          <Notice>
            <b>{l.name}</b> · stock {item !== undefined ? (stockMap.get(item.code) ?? '—') : '—'} {l.unitCode}
            · sale {inr(l.currentSalePaise)}{l.currentMrpPaise !== undefined ? ` · MRP ${inr(l.currentMrpPaise)}` : ''}
            · margin at this cost <b>{margin.toFixed(1)}%</b>
            {costHist !== undefined && costHist.length > 0 && (
              <> · last costs: {costHist.map((h) => `${inr(Number(h.cost))} (${String(h.date)})`).join(' · ')}</>
            )}
          </Notice>
        )
      })()}

      {lines.length === 0 ? (
        <EmptyState>Pick the supplier, then scan the first line of the invoice.</EmptyState>
      ) : (
        <table className="wf">
          <thead>
            <tr>
              <th>#</th><th>Item</th><th className="num">Qty</th><th className="num">Cost ₹</th>
              <th className="num">Last</th><th className="num">GST</th><th className="num">Line total</th><th>Price</th>
            </tr>
          </thead>
          <tbody>
            {lines.map((l, i) => {
              const lineTaxable = Math.round(l.qty * l.unitCostPaise)
              const lineTax = Math.round((lineTaxable * l.taxRateBp) / 10_000)
              const costChanged = l.lastCostPaise !== undefined && l.unitCostPaise !== l.lastCostPaise && l.unitCostPaise > 0
              const cell = (kind: 'qty' | 'cost') =>
                focusCell?.line === i && focusCell.cell === kind ? (
                  <input
                    ref={cellRef}
                    className="wf num" style={{ width: 90 }}
                    defaultValue={kind === 'qty' ? String(l.qty) : (l.unitCostPaise / 100).toFixed(2)}
                    onKeyDown={(e) => {
                      if (e.key !== 'Enter' && e.key !== 'Tab') return
                      e.preventDefault()
                      const v = (e.target as HTMLInputElement).value
                      if (kind === 'qty') {
                        const q = Number(v)
                        if (q > 0) setLine(i, { qty: q })
                        setFocusCell({ line: i, cell: 'cost' })
                      } else {
                        setLine(i, { unitCostPaise: rs(v) })
                        setFocusCell(undefined) // Enter on cost returns to the scan box
                      }
                    }}
                  />
                ) : (
                  <span style={{ cursor: 'pointer' }} onClick={() => setFocusCell({ line: i, cell: kind })}>
                    {kind === 'qty' ? `${l.qty} ${l.unitCode}` : inr(l.unitCostPaise)}
                  </span>
                )
              return (
                <tr key={i}>
                  <td>{i + 1}</td>
                  <td>{l.name}</td>
                  <td className="num">{cell('qty')}</td>
                  <td className="num">{cell('cost')}</td>
                  <td className="num">{l.lastCostPaise !== undefined ? inr(l.lastCostPaise) : '—'}</td>
                  <td className="num">{l.taxRateBp / 100}%</td>
                  <td className="num">{inr(lineTaxable + lineTax)}</td>
                  <td>
                    {costChanged ? (
                      <PriceRevision line={l} onSet={(p) => setLine(i, p)} />
                    ) : l.newSalePricePaise !== undefined ? (
                      <Badge tone="ok">new ₹{(l.newSalePricePaise / 100).toFixed(2)}</Badge>
                    ) : (
                      <Badge>keep</Badge>
                    )}
                  </td>
                </tr>
              )
            })}
          </tbody>
        </table>
      )}

      <Toolbar>
        <Badge>Taxable {formatINR(totals.taxable)}</Badge>
        <Badge>GST {formatINR(totals.tax)}</Badge>
        <Badge tone="accent">Total {formatINR(totals.total)}</Badge>
        <span className="spacer" />
        <Field label="Supplier's printed total ₹ (cross-check)">
          <input className="wf num" style={{ width: 150 }} value={printedTotal} onChange={(e) => setPrintedTotal(e.target.value)} />
        </Field>
        {crossCheck === 'match' && <Badge tone="ok">totals match ✓</Badge>}
        {crossCheck === 'mismatch' && <Badge tone="err">≠ computed — check a line</Badge>}
      </Toolbar>
    </div>
  )
}

/** Inline price-revision prompt when the cost moved — margin at a glance. */
function PriceRevision(props: { line: PLine; onSet: (patch: Partial<PLine>) => void }) {
  const { line } = props
  const [open, setOpen] = useState(false)
  const [sale, setSale] = useState((line.currentSalePaise / 100).toFixed(2))
  const [mrp, setMrp] = useState(line.currentMrpPaise !== undefined ? (line.currentMrpPaise / 100).toFixed(2) : '')
  const marginBp = line.unitCostPaise > 0
    ? Math.round(((line.currentSalePaise - line.unitCostPaise) / line.unitCostPaise) * 10_000)
    : 0

  if (!open) {
    return (
      <span>
        <Badge tone="warn">cost changed · margin {(marginBp / 100).toFixed(1)}%</Badge>{' '}
        <Button onClick={() => setOpen(true)}>Revise price…</Button>
      </span>
    )
  }
  return (
    <span style={{ display: 'inline-flex', gap: 6, alignItems: 'center' }}>
      <input className="wf num" style={{ width: 84 }} value={sale} onChange={(e) => setSale(e.target.value)} placeholder="Sale ₹" />
      <input className="wf num" style={{ width: 84 }} value={mrp} onChange={(e) => setMrp(e.target.value)} placeholder="MRP ₹" />
      <Button tone="primary" onClick={() => {
        props.onSet({
          newSalePricePaise: rs(sale),
          ...(mrp !== '' ? { newMrpPaise: rs(mrp) } : {}),
        })
        setOpen(false)
      }}>Set</Button>
    </span>
  )
}

export function PurchasesListPage() {
  const [data, setData] = useState<Record<string, unknown>[] | undefined>()
  const [error, setError] = useState<string | undefined>()
  useEffect(() => {
    getPurchases().then(setData).catch((e: Error) => setError(e.message))
  }, [])
  return (
    <div className="wf-page">
      <PageHeader title="Purchase List" badge="LIVE" desc="Every posted supplier invoice — duplicates are rejected at entry." />
      {error !== undefined && <Notice tone="err">{error}</Notice>}
      {data === undefined ? <EmptyState>Loading…</EmptyState> : data.length === 0 ? (
        <EmptyState>No purchases yet — enter one in Purchase Entry.</EmptyState>
      ) : (
        <DataTable
          columns={[
            { key: 'inv', label: 'Invoice' }, { key: 'supplier', label: 'Supplier' },
            { key: 'date', label: 'Invoice date' }, { key: 'lines', label: 'Lines', numeric: true },
            { key: 'total', label: 'Total', numeric: true },
          ]}
          rows={data.map((p) => ({
            inv: String(p['invoice_no']), supplier: String(p['supplier_name'] ?? ''),
            date: String(p['invoice_date']), lines: String(p['line_count']),
            total: inr(Number(p['total_paise'])),
          }))}
        />
      )}
    </div>
  )
}
