"use client"

import { useEffect, useState } from "react"

import { bankAccountsApi } from "@/lib/api/general-ledger"
import { grnsApi } from "@/lib/api/grns"
import { errorMessage } from "@/lib/error-map"
import { formatAmount } from "@/lib/format"
import { CashAndBankAccountDto } from "@/types/general-ledger"
import { Grn, GrnPayment } from "@/types/grn"

import { Button } from "@/components/ui/button"
import { Dialog, DialogContent, DialogFooter, DialogHeader, DialogTitle } from "@/components/ui/dialog"
import { Field, FieldError, FieldGroup, FieldLabel } from "@/components/ui/field"
import { Input } from "@/components/ui/input"
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select"
import { toast } from "@/components/ui/toast"

interface GrnPaymentDialogProps {
  grn: Grn | null
  open: boolean
  onOpenChange: (open: boolean) => void
  onPaid: (grn: Grn, payment: GrnPayment) => void
}

/** Pay the vendor against a confirmed GRN's balance — full or partial, from an existing
 * GL cash/bank account. Modeled on ReceivedChequeDialog's "pick an account, submit" shape. */
export function GrnPaymentDialog({ grn, open, onOpenChange, onPaid }: GrnPaymentDialogProps) {
  const [accounts, setAccounts] = useState<CashAndBankAccountDto[] | null>(null)
  const [glBankAccountId, setGlBankAccountId] = useState("")
  const [amount, setAmount] = useState("")
  const [reference, setReference] = useState("")
  const [errors, setErrors] = useState<Record<string, string>>({})
  const [submitting, setSubmitting] = useState(false)

  // Reset the form fields for a fresh open (or a different GRN) during render, React's own
  // sanctioned "adjust state while rendering" pattern (see the General Ledger report page's
  // periodKey comment) — not inside the effect below, which would be a synchronous
  // setState-in-effect (react-hooks/set-state-in-effect).
  const openKey = open && grn ? `${grn.grnId}` : null
  const [resetFor, setResetFor] = useState<string | null>(null)
  if (openKey !== null && resetFor !== openKey) {
    setResetFor(openKey)
    setAmount(grn!.balanceAmount.toFixed(2))
    setGlBankAccountId("")
    setReference("")
    setErrors({})
  }

  useEffect(() => {
    if (!open || accounts !== null) return
    bankAccountsApi.list("Both").then(setAccounts).catch(() => setAccounts([]))
  }, [open, accounts])

  if (!grn) return null

  async function submit() {
    if (!grn) return
    const nextErrors: Record<string, string> = {}
    const amountNum = Number(amount)
    if (!glBankAccountId) nextErrors.glBankAccountId = "Select an account to pay from"
    if (!amount || Number.isNaN(amountNum) || amountNum <= 0) nextErrors.amount = "Enter a valid amount"
    else if (amountNum > grn.balanceAmount) nextErrors.amount = `Cannot exceed the balance (${formatAmount(grn.balanceAmount)})`
    setErrors(nextErrors)
    if (Object.keys(nextErrors).length > 0) return

    setSubmitting(true)
    try {
      const payment = await grnsApi.pay(grn.grnId, {
        amount: amountNum,
        glBankAccountId: Number(glBankAccountId),
        reference: reference || undefined,
      })
      toast.success("Payment recorded", `${formatAmount(amountNum)} posted to the ledger (${payment.glJournalNo ?? "—"}).`)
      onPaid(grn, payment)
      onOpenChange(false)
    } catch (err) {
      toast.error("Could not record payment", errorMessage(err))
    } finally {
      setSubmitting(false)
    }
  }

  return (
    <Dialog open={open} onOpenChange={onOpenChange}>
      <DialogContent className="sm:max-w-md">
        <DialogHeader>
          <DialogTitle>Pay GRN {grn.docNo}</DialogTitle>
        </DialogHeader>

        <p className="text-sm text-muted-foreground">
          Balance due: <span className="font-medium text-foreground tabular-nums">{formatAmount(grn.balanceAmount)}</span>
        </p>

        <FieldGroup>
          <Field data-invalid={!!errors.glBankAccountId}>
            <FieldLabel htmlFor="grn-pay-account">Pay from</FieldLabel>
            <Select<string> value={glBankAccountId} onValueChange={(v) => setGlBankAccountId(v ?? "")}>
              <SelectTrigger id="grn-pay-account" className="w-full text-base" aria-invalid={!!errors.glBankAccountId}>
                <SelectValue placeholder={accounts === null ? "Loading…" : "Select a cash/bank account"} />
              </SelectTrigger>
              <SelectContent>
                {(accounts ?? []).map((a) => (
                  <SelectItem key={a.accountId} value={String(a.accountId)} label={a.accountName} className="text-base">
                    {a.accountName} ({a.accountType})
                  </SelectItem>
                ))}
              </SelectContent>
            </Select>
            <FieldError errors={[errors.glBankAccountId ? { message: errors.glBankAccountId } : undefined]} />
          </Field>

          <Field data-invalid={!!errors.amount}>
            <FieldLabel htmlFor="grn-pay-amount">Amount</FieldLabel>
            <Input
              id="grn-pay-amount"
              type="number"
              step="0.01"
              min="0.01"
              max={grn.balanceAmount}
              value={amount}
              onChange={(e) => setAmount(e.target.value)}
              aria-invalid={!!errors.amount}
            />
            <FieldError errors={[errors.amount ? { message: errors.amount } : undefined]} />
          </Field>

          <Field>
            <FieldLabel htmlFor="grn-pay-reference">Reference (optional)</FieldLabel>
            <Input id="grn-pay-reference" value={reference} onChange={(e) => setReference(e.target.value)} />
          </Field>
        </FieldGroup>

        <DialogFooter>
          <Button variant="outline" onClick={() => onOpenChange(false)} disabled={submitting}>
            Cancel
          </Button>
          <Button onClick={submit} disabled={submitting}>
            {submitting ? "Recording…" : "Record payment"}
          </Button>
        </DialogFooter>
      </DialogContent>
    </Dialog>
  )
}
