import { useForm } from '@inertiajs/react';
import { Save } from 'lucide-react';
import { useMemo, useState } from 'react';
import { toast } from 'sonner';
import { ConfirmActionDialog } from '@/components/confirm-action-dialog';
import { PurchaseBillSearchSelect } from '@/components/purchases/purchase-bill-search-select';
import { SupplierPaymentFields } from '@/components/purchases/supplier-payment-fields';
import { Button } from '@/components/ui/button';
import {
    Card,
    CardContent,
    CardDescription,
    CardHeader,
    CardTitle,
} from '@/components/ui/card';
import { Label } from '@/components/ui/label';
import {
    Select,
    SelectContent,
    SelectItem,
    SelectTrigger,
    SelectValue,
} from '@/components/ui/select';
import { cn } from '@/lib/utils';
import {
    formatMoneyAmountOrDash,
    formatPurchaseCurrencyLabel,
    formatPurchaseMoneyOrDash,
} from '@/lib/dates';
import { parseMoneyInput } from '@/lib/money';
import { buildFifoAllocationPreview } from '@/lib/supplier-fifo-allocations';
import { zodErrorToFieldMap } from '@/lib/zod-errors';
import {
    defaultPaymentMode,
    paymentModeCurrency,
    paymentModeRequiresExchange,
    resolvePaymentMode,
    toBillCurrencyAmount,
} from '@/lib/supplier-payment-mode';
import { buildSupplierPaymentApiFields } from '@/lib/supplier-payment-payload';
import { buildStoreCustomerPaymentSchema } from '@/validation/schemas';

const LOAN_CURRENCY = 'afn';
const BALANCE_CURRENCIES = ['afn', 'usd'];

function summariesByCurrency(summaries) {
    return Object.fromEntries((summaries ?? []).map((summary) => [summary.currency, summary]));
}

function todayYmd() {
    const now = new Date();
    const y = now.getFullYear();
    const m = String(now.getMonth() + 1).padStart(2, '0');
    const d = String(now.getDate()).padStart(2, '0');
    return `${y}-${m}-${d}`;
}

/**
 * @param {object} props
 * @param {Record<string, string>} props.labels
 * @param {Record<string, string>} props.validation
 * @param {Record<string, string>} props.appActions
 * @param {'en'|'fa'} props.locale
 * @param {Array<string>} props.currencies
 * @param {Array<string>} props.paymentMethods
 * @param {Array<object>} [props.summaries]
 * @param {Array<object>} props.openSales
 * @param {Array<object>} [props.openBalances]
 * @param {Record<string, Array<object>>} [props.openSalesByCurrency]
 * @param {Record<string, Array<object>>} [props.openBalancesByCurrency]
 * @param {string} props.defaultPaymentType
 * @param {string} props.submitUrl
 */
export function CustomerPaymentForm({
    labels,
    validation,
    appActions,
    locale,
    currencies,
    paymentMethods,
    summaries = [],
    openSales = [],
    openBalances = [],
    openSalesByCurrency = {},
    openBalancesByCurrency = {},
    defaultPaymentType = 'sale',
    submitUrl,
}) {
    const initialSale =
        openSales.length > 0 ? openSales[0] : null;
    const initialSaleId = initialSale ? String(initialSale.id) : '';
    const initialBillCurrency = initialSale?.currency ?? currencies[0] ?? LOAN_CURRENCY;

    const form = useForm({
        payment_type: defaultPaymentType,
        sale_id: initialSaleId,
        bill_currency: initialBillCurrency,
        currency: paymentModeCurrency(defaultPaymentMode(initialBillCurrency)),
        amount: '',
        paid_at: todayYmd(),
        payment_method: 'cash',
        reference: '',
        notes: '',
        exchange_rate: '',
    });
    const [confirmOpen, setConfirmOpen] = useState(false);

    const schema = useMemo(
        () => buildStoreCustomerPaymentSchema(validation),
        [validation],
    );

    const selectedSale = useMemo(
        () => openSales.find((bill) => String(bill.id) === String(form.data.sale_id)),
        [openSales, form.data.sale_id],
    );

    const billCurrency =
        form.data.payment_type === 'sale'
            ? (selectedSale?.currency ?? form.data.bill_currency ?? LOAN_CURRENCY)
            : (form.data.bill_currency ?? LOAN_CURRENCY);

    const openBillsForOverall = useMemo(() => {
        // Loans are AFN-only: include them only when paying AFN balances.
        if (billCurrency === LOAN_CURRENCY) {
            return (
                openBalancesByCurrency[LOAN_CURRENCY] ??
                openBalances ??
                openSalesByCurrency[LOAN_CURRENCY] ??
                []
            );
        }

        return openSalesByCurrency[billCurrency] ?? [];
    }, [
        billCurrency,
        openBalancesByCurrency,
        openBalances,
        openSalesByCurrency,
    ]);

    const overallDueAmount = useMemo(() => {
        const summary = summariesByCurrency(summaries)[billCurrency];

        if (summary) {
            if (billCurrency === LOAN_CURRENCY) {
                return Number(summary.outstanding ?? 0);
            }

            return Number(summary.purchase_outstanding ?? summary.outstanding ?? 0);
        }

        return openBillsForOverall.reduce(
            (total, row) => total + Number(row.due_amount ?? 0),
            0,
        );
    }, [summaries, billCurrency, openBillsForOverall]);

    const fifoPreview = useMemo(() => {
        if (form.data.payment_type !== 'overall') {
            return null;
        }

        const enteredAmount = parseMoneyInput(form.data.amount);

        if (enteredAmount == null || enteredAmount <= 0) {
            return null;
        }

        const paymentMode = resolvePaymentMode(form.data.currency, billCurrency);
        let billAmount = enteredAmount;

        if (paymentModeRequiresExchange(paymentMode)) {
            const exchangeRate = parseMoneyInput(form.data.exchange_rate);

            if (exchangeRate == null || exchangeRate <= 0) {
                return null;
            }

            billAmount = toBillCurrencyAmount(
                enteredAmount,
                paymentModeCurrency(paymentMode),
                billCurrency,
                exchangeRate,
            );
        }

        return buildFifoAllocationPreview(openBillsForOverall, billAmount);
    }, [
        form.data.payment_type,
        form.data.amount,
        form.data.currency,
        form.data.exchange_rate,
        billCurrency,
        openBillsForOverall,
    ]);

    function handlePaymentFieldChange(field, value) {
        form.setData(field, value);
    }

    function buildPayload() {
        const paymentFields = buildSupplierPaymentApiFields(form.data, billCurrency);

        const saleId =
            form.data.payment_type === 'sale' && form.data.sale_id !== ''
                ? Number(form.data.sale_id)
                : null;

        return {
            payment_type: form.data.payment_type,
            sale_id: saleId,
            bill_currency:
                form.data.payment_type === 'overall'
                    ? form.data.bill_currency
                    : billCurrency,
            ...paymentFields,
        };
    }

    function applyValidationErrors(parsed) {
        form.setError(zodErrorToFieldMap(parsed.error));
        const firstMessage = parsed.error.issues[0]?.message;
        if (firstMessage) {
            toast.error(firstMessage);
        }
    }

    function handleSubmit(e) {
        e.preventDefault();
        form.clearErrors();
        const parsed = schema.safeParse(buildPayload());
        if (!parsed.success) {
            applyValidationErrors(parsed);
            return;
        }
        setConfirmOpen(true);
    }

    function commitSave() {
        form.clearErrors();
        const parsed = schema.safeParse(buildPayload());
        if (!parsed.success) {
            applyValidationErrors(parsed);
            setConfirmOpen(false);
            return;
        }

        form.transform(() => parsed.data);
        form.post(submitUrl, {
            onSuccess: () => setConfirmOpen(false),
            onError: () => toast.error(appActions.requestFailed),
        });
    }

    return (
        <>
            <ConfirmActionDialog
                open={confirmOpen}
                onOpenChange={setConfirmOpen}
                title={labels.confirmSaveTitle}
                description={labels.confirmSaveBody}
                confirmLabel={labels.savePayment}
                cancelLabel={appActions.cancel}
                processing={form.processing}
                onConfirm={commitSave}
            />

            <form onSubmit={handleSubmit} className="mx-auto flex w-full max-w-4xl flex-col gap-6">
                <Card>
                    <CardHeader>
                        <CardTitle>{labels.createHeadTitle}</CardTitle>
                        <CardDescription>{labels.createDescription}</CardDescription>
                        {summaries.length > 0 ? (
                            <div className="mt-3 grid gap-3 sm:grid-cols-2">
                                {BALANCE_CURRENCIES.map((currency) => {
                                    const summary = summariesByCurrency(summaries)[currency];
                                    const purchaseDue = Number(
                                        summary?.purchase_outstanding ?? 0,
                                    );
                                    const loanDue =
                                        currency === LOAN_CURRENCY
                                            ? Number(summary?.loan_outstanding ?? 0)
                                            : 0;
                                    const overallDue =
                                        currency === LOAN_CURRENCY
                                            ? Number(summary?.outstanding ?? 0)
                                            : purchaseDue;
                                    const showLoan = currency === LOAN_CURRENCY;

                                    return (
                                        <div
                                            key={currency}
                                            className="space-y-2 rounded-lg border border-border bg-muted/20 px-3 py-2 text-sm"
                                        >
                                            <p className="text-muted-foreground text-xs font-medium">
                                                {formatPurchaseCurrencyLabel(currency, locale)}
                                            </p>
                                            <div
                                                className={cn(
                                                    'grid gap-2',
                                                    showLoan ? 'sm:grid-cols-1' : 'sm:grid-cols-1',
                                                )}
                                            >
                                                <div>
                                                    <span className="text-muted-foreground">
                                                        {labels.purchaseBalanceLabel}:{' '}
                                                    </span>
                                                    <span className="font-semibold tabular-nums">
                                                        {formatMoneyAmountOrDash(
                                                            purchaseDue,
                                                            locale,
                                                        )}
                                                    </span>
                                                </div>
                                                {showLoan ? (
                                                    <div>
                                                        <span className="text-muted-foreground">
                                                            {labels.loanBalanceLabel}:{' '}
                                                        </span>
                                                        <span className="font-semibold tabular-nums">
                                                            {formatMoneyAmountOrDash(
                                                                loanDue,
                                                                locale,
                                                            )}
                                                        </span>
                                                    </div>
                                                ) : (
                                                    <div>
                                                        <span className="text-muted-foreground">
                                                            {labels.loanBalanceLabel}:{' '}
                                                        </span>
                                                        <span className="text-muted-foreground text-xs">
                                                            {labels.loanAfnOnlyHint}
                                                        </span>
                                                    </div>
                                                )}
                                                <div>
                                                    <span className="text-muted-foreground">
                                                        {labels.overallBalanceLabel}:{' '}
                                                    </span>
                                                    {overallDue > 0.001 ? (
                                                        <span className="font-semibold tabular-nums text-amber-700 dark:text-amber-400">
                                                            {formatMoneyAmountOrDash(
                                                                overallDue,
                                                                locale,
                                                            )}
                                                        </span>
                                                    ) : (
                                                        <span className="text-muted-foreground">
                                                            {labels.balanceClear}
                                                        </span>
                                                    )}
                                                </div>
                                            </div>
                                        </div>
                                    );
                                })}
                            </div>
                        ) : null}
                    </CardHeader>
                    <CardContent className="space-y-6">
                        <div className="space-y-2">
                            <Label>{labels.paymentTypeLabel}</Label>
                            <div className="grid grid-cols-2 gap-2 rounded-lg border border-border p-1">
                                {['sale', 'overall'].map((type) => (
                                    <Button
                                        key={type}
                                        type="button"
                                        variant={
                                            form.data.payment_type === type ? 'default' : 'ghost'
                                        }
                                        className={cn('h-9')}
                                        onClick={() =>
                                            form.setData((data) => ({
                                                ...data,
                                                payment_type: type,
                                                exchange_rate: '',
                                            }))
                                        }
                                    >
                                        {type === 'sale'
                                            ? labels.paymentTypeSale
                                            : labels.paymentTypeOverall}
                                    </Button>
                                ))}
                            </div>
                        </div>

                        {form.data.payment_type === 'sale' ? (
                            <div className="space-y-4 rounded-xl border border-border p-4">
                                <div>
                                    <h3 className="text-sm font-semibold">{labels.billSection}</h3>
                                    <p className="text-muted-foreground text-sm">
                                        {labels.paymentTypeSaleHint}
                                    </p>
                                </div>

                                <div className="grid gap-6 lg:grid-cols-2">
                                    <div className="space-y-2">
                                        <Label htmlFor="payment-sale-bill">
                                            {labels.saleBill}
                                        </Label>
                                        {openSales.length === 0 ? (
                                            <p className="text-muted-foreground text-sm">
                                                {labels.noOpenBills}
                                            </p>
                                        ) : (
                                            <PurchaseBillSearchSelect
                                                id="payment-sale-bill"
                                                value={form.data.sale_id}
                                                bills={openSales}
                                                labels={{
                                                    ...labels,
                                                    purchaseBill: labels.saleBill,
                                                    purchaseBillPlaceholder: labels.saleBillPlaceholder,
                                                    purchaseBillSearchPlaceholder:
                                                        labels.saleBillSearchPlaceholder,
                                                    purchaseBillSearchEmpty:
                                                        labels.saleBillSearchEmpty,
                                                }}
                                                locale={locale}
                                                invalid={Boolean(form.errors.sale_id)}
                                            onValueChange={(value) => {
                                                const bill = openSales.find(
                                                    (row) => String(row.id) === String(value),
                                                );
                                                const billCur = bill?.currency ?? 'afn';

                                                form.setData((data) => ({
                                                    ...data,
                                                    sale_id: value,
                                                    currency: paymentModeCurrency(
                                                        defaultPaymentMode(billCur),
                                                    ),
                                                    exchange_rate: '',
                                                }));
                                            }}
                                            />
                                        )}
                                        {form.errors.sale_id ? (
                                            <p className="text-destructive text-sm">
                                                {form.errors.sale_id}
                                            </p>
                                        ) : null}
                                    </div>

                                    {selectedSale ? (
                                        <div className="rounded-lg border border-border bg-muted/20 p-3 text-sm">
                                            <p>
                                                <span className="text-muted-foreground">
                                                    {labels.dueLabel}:{' '}
                                                </span>
                                                <span className="font-medium tabular-nums">
                                                    {formatMoneyAmountOrDash(
                                                        selectedSale.due_amount,
                                                        locale,
                                                    )}
                                                </span>
                                            </p>
                                        </div>
                                    ) : null}
                                </div>
                            </div>
                        ) : null}

                        <div className="space-y-4 rounded-xl border border-border p-4">
                            <div>
                                <h3 className="text-sm font-semibold">
                                    {labels.paymentDetailsSection}
                                </h3>
                                {form.data.payment_type === 'overall' ? (
                                    <div className="text-muted-foreground space-y-1 text-sm">
                                        <p>{labels.paymentTypeOverallHint}</p>
                                    </div>
                                ) : null}
                            </div>

                            {form.data.payment_type === 'overall' ? (
                                <div className="grid gap-6 sm:grid-cols-2">
                                    <div className="space-y-2">
                                        <Label htmlFor="overall-bill-currency">
                                            {labels.billCurrency}
                                        </Label>
                                        <Select
                                            value={form.data.bill_currency}
                                            onValueChange={(value) =>
                                                form.setData((data) => ({
                                                    ...data,
                                                    bill_currency: value,
                                                    currency: paymentModeCurrency(
                                                        defaultPaymentMode(value),
                                                    ),
                                                    exchange_rate: '',
                                                }))
                                            }
                                        >
                                            <SelectTrigger
                                                id="overall-bill-currency"
                                                aria-invalid={Boolean(form.errors.bill_currency)}
                                                className={cn(
                                                    form.errors.bill_currency && 'border-destructive',
                                                )}
                                            >
                                                <SelectValue />
                                            </SelectTrigger>
                                            <SelectContent>
                                                {currencies.map((currency) => (
                                                    <SelectItem key={currency} value={currency}>
                                                        {formatPurchaseCurrencyLabel(
                                                            currency,
                                                            locale,
                                                        )}
                                                    </SelectItem>
                                                ))}
                                            </SelectContent>
                                        </Select>
                                        <p className="text-muted-foreground text-xs">
                                            {form.data.bill_currency === LOAN_CURRENCY
                                                ? labels.billCurrencyAfnHint ??
                                                  labels.billCurrencyHint
                                                : labels.billCurrencyUsdHint ??
                                                  labels.billCurrencyHint}
                                        </p>
                                        {form.errors.bill_currency ? (
                                            <p className="text-destructive text-sm">
                                                {form.errors.bill_currency}
                                            </p>
                                        ) : null}
                                    </div>
                                </div>
                            ) : null}

                            <SupplierPaymentFields
                                labels={labels}
                                locale={locale}
                                currencies={currencies}
                                paymentMethods={paymentMethods}
                                billCurrency={billCurrency}
                                maxBillAmount={
                                    form.data.payment_type === 'sale' && selectedSale
                                        ? Number(selectedSale.due_amount)
                                        : form.data.payment_type === 'overall'
                                          ? overallDueAmount > 0.001
                                              ? overallDueAmount
                                              : null
                                          : null
                                }
                                data={form.data}
                                errors={form.errors}
                                onChange={handlePaymentFieldChange}
                                idPrefix="supplier-payment"
                            />

                            {form.data.payment_type === 'overall' ? (
                                <div className="space-y-3 rounded-lg border border-border bg-muted/20 p-3 text-sm">
                                    <div>
                                        <p className="font-medium">{labels.fifoSection}</p>
                                        <p className="text-muted-foreground text-xs">
                                            {labels.fifoSectionHint}
                                        </p>
                                    </div>
                                    {openBillsForOverall.length === 0 ? (
                                        <p className="text-muted-foreground text-sm">
                                            {labels.noOpenBalances}
                                        </p>
                                    ) : (
                                        <ol className="space-y-2">
                                            {openBillsForOverall.map((bill, index) => {
                                                const allocation = fifoPreview?.allocations.find(
                                                    (row) =>
                                                        row.bill.id === bill.id &&
                                                        (row.bill.type ?? 'sale') ===
                                                            (bill.type ?? 'sale'),
                                                );
                                                const itemLabel =
                                                    bill.type === 'loan'
                                                        ? labels.fifoLoanOrderLabel
                                                        : labels.fifoOrderLabel;

                                                return (
                                                    <li
                                                        key={`${bill.type ?? 'sale'}-${bill.id}`}
                                                        className="flex flex-wrap items-center justify-between gap-2 rounded-md border border-border bg-background px-3 py-2"
                                                    >
                                                        <div>
                                                            <span className="text-muted-foreground text-xs">
                                                                {itemLabel.replace(
                                                                    '{order}',
                                                                    String(index + 1),
                                                                )}
                                                            </span>
                                                            <p className="font-medium">
                                                                {bill.bill_number ?? bill.label}
                                                            </p>
                                                        </div>
                                                        <div className="text-end tabular-nums">
                                                            <p className="text-muted-foreground text-xs">
                                                                {labels.dueLabel}
                                                            </p>
                                                            <p>
                                                                {formatPurchaseMoneyOrDash(
                                                                    bill.due_amount,
                                                                    billCurrency,
                                                                    locale,
                                                                )}
                                                            </p>
                                                            {allocation ? (
                                                                <p className="font-medium text-emerald-700 dark:text-emerald-400">
                                                                    {labels.fifoWillApply}:{' '}
                                                                    {formatPurchaseMoneyOrDash(
                                                                        allocation.amount,
                                                                        billCurrency,
                                                                        locale,
                                                                    )}
                                                                </p>
                                                            ) : (
                                                                <p className="text-muted-foreground text-xs">
                                                                    {labels.fifoSkipped}
                                                                </p>
                                                            )}
                                                        </div>
                                                    </li>
                                                );
                                            })}
                                        </ol>
                                    )}
                                    {fifoPreview && fifoPreview.remainingCredit > 0.001 ? (
                                        <p className="text-muted-foreground text-xs">
                                            {labels.fifoRemainingUnapplied}:{' '}
                                            <span className="font-medium tabular-nums">
                                                {formatPurchaseMoneyOrDash(
                                                    fifoPreview.remainingCredit,
                                                    billCurrency,
                                                    locale,
                                                )}
                                            </span>
                                        </p>
                                    ) : null}
                                </div>
                            ) : null}
                        </div>

                        <div className="flex flex-wrap items-center gap-3 border-t border-border pt-4">
                            <Button
                                type="submit"
                disabled={
                                    form.processing ||
                                    (form.data.payment_type === 'sale' &&
                                        openSales.length === 0) ||
                                    (form.data.payment_type === 'overall' &&
                                        openBillsForOverall.length === 0)
                                }
                            >
                                <Save className="size-4" />
                                {form.processing ? labels.savingPayment : labels.savePayment}
                            </Button>
                        </div>
                    </CardContent>
                </Card>
            </form>
        </>
    );
}
