import { useEffect } from 'react'
import { useTranslation } from 'react-i18next'
import { createPortal } from 'react-dom'
import { Barcode } from '@/components/ui'
import { formatMoney, type Money } from '@/lib/money'
import { formatDateTime } from '@/lib/format'
import type { ReceiptSettings } from '@/features/settings/hooks'
import { usePrintHardware } from './usePrintHardware'
import { taxLineLabel } from './taxLabel'
import { useAuth } from '@/features/auth'

/**
 * The printable customer receipt.
 *
 * Portalled to <body> as a sibling of the SPA root so print CSS can hide
 * #root and lay out only this (see styles/print.css) — no popup window (which
 * browsers block) and no iframe. Hidden on screen; it exists purely so
 * window.print() has something to render.
 *
 * Every toggle here comes from the store's Receipt Designer (Settings →
 * Receipt). Those settings persisted correctly for a long time but nothing
 * ever consumed them — this component is the consumer, so turning off
 * "show cashier" or switching to a 58 mm roll now genuinely changes what
 * prints.
 *
 * The paper the receipt is laid out for no longer comes from the designer
 * alone: usePrintHardware resolves the counter's hardware override first, then
 * the store's Settings → Hardware, and only then the designer's width (that
 * hook's docblock explains the order). `data-paper` and `data-mode` carry the
 * result into print.css, which sizes the printed page from them.
 */

/** The subset of a sale this receipt needs — satisfied by both the POS's
 *  PosSale and the Sales-detail SaleDetail shapes. */
export interface ReceiptSale {
  number: string
  created_at?: string | null
  customer_name?: string | null
  customer_phone?: string | null
  cashier_name?: string | null
  /** The counter this sale was rung up on — picks its hardware for a reprint. */
  counter_id?: number | null
  /**
   * The outlet this sale was rung at, frozen on the sale itself.
   *
   * NOT the caller's active branch: Sale Detail reprints old sales, and an
   * admin looking at another outlet's sale must not stamp their own branch onto
   * the receipt.
   */
  branch_name?: string | null
  branch_code?: string | null
  sub_total: Money
  discount?: { amount: Money } | null
  charges?: { charge_id: number | null; name: string; kind: string; rate: number | null; amount: Money }[]
  tax_total: Money
  tax_lines?: { name: string; component?: string | null; rate_bp?: number; amount: Money }[]
  /** Tax grouped by the commodity code frozen on each line (India: HSN). */
  hsn_summary?: { code: string; taxable: Money; tax: Money; components: { name: string; rate_bp: number; amount: Money }[] }[]
  /**
   * Cash rounding on the bill, signed and already inside `total`. Frozen on the
   * sale, so a reprint prints the same line as the slip handed over.
   */
  rounding?: Money | null
  total: Money
  paid: Money
  due: Money
  /** Money handed back. Frozen at checkout; 0 on sales rung before it was kept. */
  change_due?: Money | null
  payments?: { id: number; method: string; amount: Money }[]
  lines: { id?: number; name: string | null; sku?: string | null; tax_code?: string | null; quantity: number | string; unit_price: Money; line_total: Money }[]
}

const METHOD_LABEL: Record<string, string> = {
  cash: 'Cash',
  upi: 'UPI',
  card: 'Card',
  bank: 'Bank transfer',
}

export function SaleReceipt({
  sale,
  settings,
}: {
  sale: ReceiptSale | null
  settings?: ReceiptSettings
}) {
  // Hooks before the early return — this component is rendered with a null sale
  // for most of the till's life.
  const { t } = useTranslation()
  const hardware = usePrintHardware(sale?.counter_id)
  // From /me, which every signed-in user already has — including the cashier,
  // who is the one printing. See the contract note on store.letterhead.
  const { me } = useAuth()
  const letterhead = me?.store?.letterhead

  // Every hook must run on every render, so this is a flag rather than an early
  // return. It used to be `if (!sale …) return null` ABOVE the useEffect below,
  // which meant the hook count changed the moment `sale` arrived — React error
  // #310, caught by the router's boundary, which replaced the page with the
  // error screen. `sale` going from null to loaded is the NORMAL path here: it
  // is how Sale Detail resolves and how the POS post-sale modal opens.
  const mounted = Boolean(sale) && typeof document !== 'undefined'

  // Tell the stylesheet a receipt is mounted. `print.css` hides the whole SPA
  // while printing so only the receipt lays out — which is right here and blank
  // everywhere else: any other page with a Print button (the superadmin invoice,
  // for one) printed an empty sheet, because nothing was left on it.
  useEffect(() => {
    if (!mounted) return

    document.body.classList.add('printing-receipt')

    return () => document.body.classList.remove('printing-receipt')
  }, [mounted])

  // `sale` is named again so TypeScript narrows it — `mounted` is a boolean
  // and carries no type information about what made it true.
  if (!mounted || !sale) return null

  // Counter override → resolved printer → Receipt Designer → the standard roll.
  const paper = hardware.paper_width ?? settings?.paper_width ?? '80mm'
  // Always resolvable — see usePrintHardware.ts: thermal only when a real
  // direct-print printer resolved for this counter.
  const mode = hardware.printer_mode
  const font = settings?.font_size ?? 'md'
  const savings = sale.discount?.amount.amount ?? 0
  // The GSTIN / HSN options are an Indian store's: the designer only offers them
  // there, and they are honoured only there, so a store that later changes
  // country cannot keep printing "GSTIN:" from a switch it can no longer see.
  const isIndia = letterhead?.country_code === 'IN'

  return createPortal(
    <div className="receipt-print-root" data-paper={paper} data-mode={mode} data-font={font}>
      <div className="receipt-sheet">
        {settings?.show_logo && settings.logo_url && (
          <img className="receipt-logo" src={settings.logo_url} alt="" />
        )}

        {settings?.header_text && (
          <div className="receipt-center receipt-strong receipt-pre">{settings.header_text}</div>
        )}
        {/* The shop's postal address and phone, from Settings -> Store profile.
            Composed server-side (/me -> store.letterhead) so the thermal
            receipt, and any invoice built later, cannot each invent their own
            idea of where the comma goes. Renders only the lines that are filled,
            so a store that has not completed its profile prints exactly what it
            printed before. */}
        {settings?.show_address !== false && letterhead && (
          <>
            {(letterhead.address_lines ?? []).map((line) => (
              <div key={line} className="receipt-center receipt-muted">{line}</div>
            ))}
            {letterhead.phone && (
              <div className="receipt-center receipt-muted">{letterhead.phone}</div>
            )}
            {letterhead.website && (
              <div className="receipt-center receipt-muted">{letterhead.website}</div>
            )}
          </>
        )}
        {/* Which outlet rang it. Name and code together: a bare "KOVA" tells a
            customer nothing, and the name alone is what staff cannot reconcile
            against a code on a purchase order. Off unless the shop asks. */}
        {settings?.show_branch && (sale.branch_name || sale.branch_code) && (
          <div className="receipt-center receipt-muted">
            {[sale.branch_name, sale.branch_code].filter(Boolean).join(' · ')}
          </div>
        )}
        {/* The store's registration number, or this branch's if it files
            separately — resolved server-side. Receipt Designer used to keep its
            OWN tax_id, a third copy that could disagree with Store profile. */}
        {settings?.show_tax_number !== false && letterhead?.tax_id && (
          <div className="receipt-center receipt-muted">
            {isIndia && settings?.show_gstin_label ? `GSTIN: ${letterhead.tax_id}` : letterhead.tax_id}
          </div>
        )}

        <hr className="receipt-rule" />

        <div className="receipt-row">
          <span>{sale.number}</span>
          <span>{formatDateTime(sale.created_at, '')}</span>
        </div>
        {settings?.show_cashier && sale.cashier_name && (
          <div className="receipt-row receipt-muted">
            <span>{t('Served by')}</span>
            <span>{sale.cashier_name}</span>
          </div>
        )}
        {settings?.show_customer && sale.customer_name && (
          <div className="receipt-row receipt-muted">
            <span>{t('Customer')}</span>
            <span>{sale.customer_name}</span>
          </div>
        )}
        {settings?.show_customer_phone && sale.customer_phone && (
          <div className="receipt-row receipt-muted">
            <span>{t('Phone')}</span>
            {/* Forced LTR: a phone number inside an Arabic receipt reverses
                otherwise, and a reversed number is worse than none. */}
            <span dir="ltr">{sale.customer_phone}</span>
          </div>
        )}

        <hr className="receipt-rule" />

        <table className="receipt-items">
          <tbody>
            {sale.lines.map((l, i) => (
              <tr key={l.id ?? i}>
                <td className="receipt-qty">{l.quantity}</td>
                <td>
                  {l.name ?? ''}
                  {settings?.show_sku && l.sku && (
                    <span className="receipt-sku">{l.sku}</span>
                  )}
                  {isIndia && settings?.show_hsn && l.tax_code && (
                    <span className="receipt-sku">HSN: {l.tax_code}</span>
                  )}
                </td>
                <td className="receipt-amt">{formatMoney(l.line_total)}</td>
              </tr>
            ))}
          </tbody>
        </table>

        <hr className="receipt-rule" />

        <div className="receipt-totals">
          <div className="receipt-row">
            <span>{t('Subtotal')}</span>
            <span>{formatMoney(sale.sub_total)}</span>
          </div>

          {savings > 0 && (
            <div className="receipt-row">
              <span>{t('Discount')}</span>
              <span>-{formatMoney(sale.discount!.amount)}</span>
            </div>
          )}

          {sale.charges?.map((c, i) => (
            <div className="receipt-row" key={c.charge_id ?? i}>
              <span>
                {c.name}
                {c.kind === 'percent' && c.rate !== null ? ` (${c.rate}%)` : ''}
              </span>
              <span>{formatMoney(c.amount)}</span>
            </div>
          ))}

          {settings?.show_tax_breakup && sale.tax_lines?.length
            ? sale.tax_lines.map((tl, i) => (
                <div className="receipt-row receipt-muted" key={`${tl.name}|${tl.component ?? ''}|${i}`}>
                  <span>{taxLineLabel(tl)}</span>
                  <span>{formatMoney(tl.amount)}</span>
                </div>
              ))
            : sale.tax_total.amount > 0 && (
                <div className="receipt-row receipt-muted">
                  <span>{t('Tax')}</span>
                  <span>{formatMoney(sale.tax_total)}</span>
                </div>
              )}

          {(sale.rounding?.amount ?? 0) !== 0 && (
            <div className="receipt-row">
              <span>{t('Rounding')}</span>
              <span>{formatMoney(sale.rounding!)}</span>
            </div>
          )}

          <hr className="receipt-rule" />

          <div className="receipt-row receipt-strong">
            <span>{t('TOTAL')}</span>
            <span>{formatMoney(sale.total)}</span>
          </div>

          {settings?.show_payment_details !== false &&
            sale.payments?.map((p) => (
              <div className="receipt-row" key={p.id}>
                <span>{METHOD_LABEL[p.method] ?? p.method}</span>
                <span>{formatMoney(p.amount)}</span>
              </div>
            ))}

          {/*
            Money handed back. Not behind a toggle and not optional: a customer
            who gave 50.00 for a 39.00 bill needs the 11.00 on the slip, and no
            shopkeeper wants to hide it. Renders only when there IS change, so
            every sale rung before the figure was kept prints exactly as before.

            The tender rows above are capped at the total (§12.4), so "Cash
            39.00 / Change 11.00" is the honest reading: what was applied to the
            sale, then what went back across the counter.
          */}
          {(sale.change_due?.amount ?? 0) > 0 && (
            <div className="receipt-row receipt-strong">
              <span>{t('Change due')}</span>
              <span>{formatMoney(sale.change_due!)}</span>
            </div>
          )}

          {sale.due.amount > 0 && (
            <div className="receipt-row receipt-strong">
              <span>{t('Balance due')}</span>
              <span>{formatMoney(sale.due)}</span>
            </div>
          )}

          {settings?.show_payment_status && (
            <div className="receipt-row receipt-strong">
              <span>{t('Payment')}</span>
              <span>{sale.due.amount > 0 ? 'UNPAID' : 'PAID'}</span>
            </div>
          )}

          {settings?.show_savings && savings > 0 && (
            <>
              <hr className="receipt-rule" />
              <div className="receipt-center">
                You saved {formatMoney(sale.discount!.amount)} on this bill
              </div>
            </>
          )}
        </div>

        <hr className="receipt-rule" />

        {/*
          The receipt number as something the returns desk can scan rather than
          key in. Rendered from the number already printed at the top, so there
          is nothing extra to keep in step with it.
        */}
        {settings?.show_barcode && sale.number && (
          <div className="receipt-center receipt-barcode">
            <Barcode value={sale.number} height={38} showText={false} />
          </div>
        )}

        {isIndia && settings?.show_hsn_summary && sale.hsn_summary && sale.hsn_summary.length > 0 && (
          <>
            <div className="receipt-strong">{t('HSN-wise tax summary')}</div>
            {sale.hsn_summary.map((g) => (
              <div key={g.code || 'none'} className="receipt-hsn">
                <div className="receipt-row">
                  <span>{g.code ? `HSN ${g.code}` : t('No HSN')}</span>
                  <span>{formatMoney(g.taxable)}</span>
                </div>
                {g.components.map((c) => (
                  <div className="receipt-row receipt-muted" key={`${c.name}|${c.rate_bp}`}>
                    <span>{taxLineLabel({ name: c.name, component: c.name, rate_bp: c.rate_bp })}</span>
                    <span>{formatMoney(c.amount)}</span>
                  </div>
                ))}
              </div>
            ))}
            <hr className="receipt-rule" />
          </>
        )}

        {settings?.footer_text && <div className="receipt-center receipt-pre">{settings.footer_text}</div>}
        {settings?.return_policy && (
          <div className="receipt-center receipt-muted receipt-pre">{settings.return_policy}</div>
        )}
      </div>
    </div>,
    document.body,
  )
}
