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 { AppDateInput } from '@/components/ui/app-date-input';
import { Button } from '@/components/ui/button';
import {
    Card,
    CardContent,
    CardDescription,
    CardHeader,
    CardTitle,
} from '@/components/ui/card';
import { Input } from '@/components/ui/input';
import { Label } from '@/components/ui/label';
import {
    Select,
    SelectContent,
    SelectItem,
    SelectTrigger,
    SelectValue,
} from '@/components/ui/select';
import { Spinner } from '@/components/ui/spinner';
import { formatAppDateOrDash, formatMoneyAmountOrDash } from '@/lib/dates';
import { parseMoneyInput, sanitizeDecimalInput } from '@/lib/money';
import { buildFifoAllocationPreview } from '@/lib/supplier-fifo-allocations';
import { cn } from '@/lib/utils';
import { zodErrorToFieldMap } from '@/lib/zod-errors';
import { z } from 'zod';

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}`;
}

function paymentMethodLabel(method, labels) {
    if (method === 'bank') {
        return labels.methodBank;
    }

    if (method === 'other') {
        return labels.methodOther;
    }

    return labels.methodCash;
}

/**
 * @param {object} props
 */
export function SarafPaymentForm({
    labels,
    validation,
    appActions,
    locale,
    paymentMethods,
    openExchanges = [],
    summary,
    defaultPaymentType = 'exchange',
    submitUrl,
}) {
    const initialExchange = openExchanges.length > 0 ? openExchanges[0] : null;
    const initialExchangeId = initialExchange ? String(initialExchange.id) : '';

    const form = useForm({
        payment_type: defaultPaymentType,
        currency_exchange_id: initialExchangeId,
        amount: '',
        paid_at: todayYmd(),
        payment_method: 'cash',
        reference: '',
        notes: '',
    });
    const [confirmOpen, setConfirmOpen] = useState(false);

    const schema = useMemo(
        () =>
            z
                .object({
                    payment_type: z.enum(['exchange', 'general']),
                    currency_exchange_id: z.coerce.number().int().positive().optional().nullable(),
                    amount: z.coerce.number().gt(0, validation.required),
                    paid_at: z.string().min(1, validation.required),
                    payment_method: z.string().optional().nullable(),
                    reference: z.string().optional().nullable(),
                    notes: z.string().optional().nullable(),
                })
                .superRefine((data, ctx) => {
                    if (data.payment_type === 'exchange' && !data.currency_exchange_id) {
                        ctx.addIssue({
                            code: 'custom',
                            message: validation.required,
                            path: ['currency_exchange_id'],
                        });
                    }
                }),
        [validation.required],
    );

    const selectedExchange = useMemo(
        () =>
            openExchanges.find(
                (exchange) => String(exchange.id) === String(form.data.currency_exchange_id),
            ),
        [openExchanges, form.data.currency_exchange_id],
    );

    const maxAmount = useMemo(() => {
        if (form.data.payment_type === 'exchange') {
            return parseMoneyInput(selectedExchange?.due_amount ?? selectedExchange?.remain_dollar_on_saraf);
        }

        return parseMoneyInput(summary?.remain_dollar_on_saraf);
    }, [form.data.payment_type, selectedExchange, summary?.remain_dollar_on_saraf]);

    const fifoPreview = useMemo(() => {
        if (form.data.payment_type !== 'general') {
            return null;
        }

        const enteredAmount = parseMoneyInput(form.data.amount);

        if (enteredAmount == null || enteredAmount <= 0) {
            return null;
        }

        return buildFifoAllocationPreview(openExchanges, enteredAmount);
    }, [form.data.payment_type, form.data.amount, openExchanges]);

    const canSubmit =
        openExchanges.length > 0 &&
        maxAmount != null &&
        maxAmount > 0.001 &&
        !form.processing;

    function handleSubmit(e) {
        e.preventDefault();
        form.clearErrors();
        const parsed = schema.safeParse(form.data);

        if (!parsed.success) {
            form.setError(zodErrorToFieldMap(parsed.error));
            return;
        }

        const amount = parseMoneyInput(form.data.amount);

        if (amount != null && maxAmount != null && amount > maxAmount + 0.001) {
            form.setError('amount', labels.amountExceedsRemain);
            return;
        }

        setConfirmOpen(true);
    }

    function commitSave() {
        form.clearErrors();
        const parsed = schema.safeParse(form.data);

        if (!parsed.success) {
            form.setError(zodErrorToFieldMap(parsed.error));
            setConfirmOpen(false);
            return;
        }

        form.post(submitUrl, {
            preserveScroll: true,
            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}
            />

            <Card>
                <CardHeader>
                    <CardTitle>{labels.createTitle}</CardTitle>
                    <CardDescription>{labels.createDescription}</CardDescription>
                </CardHeader>
                <CardContent>
                    <form noValidate onSubmit={handleSubmit} className="space-y-6">
                        <div className="grid gap-4 sm:grid-cols-2 lg:grid-cols-3">
                            <div className="rounded-xl border border-border/80 bg-muted/15 p-4">
                                <p className="text-muted-foreground text-sm">{labels.remainUsdLabel}</p>
                                <p className="mt-1 text-lg font-semibold tabular-nums text-amber-700 dark:text-amber-400">
                                    {formatMoneyAmountOrDash(summary?.remain_dollar_on_saraf, locale)}{' '}
                                    {labels.usdCurrencyLabel}
                                </p>
                            </div>
                        </div>

                        {openExchanges.length === 0 ? (
                            <p className="text-muted-foreground rounded-lg border border-dashed px-4 py-6 text-center text-sm">
                                {labels.noOpenExchanges}
                            </p>
                        ) : (
                            <>
                                <div className="space-y-2">
                                    <Label>{labels.paymentTypeLabel}</Label>
                                    <div className="grid grid-cols-2 gap-2 rounded-lg border border-border p-1">
                                        {['exchange', 'general'].map((type) => (
                                            <Button
                                                key={type}
                                                type="button"
                                                variant={
                                                    form.data.payment_type === type ? 'default' : 'ghost'
                                                }
                                                className="h-9"
                                                onClick={() =>
                                                    form.setData((data) => ({
                                                        ...data,
                                                        payment_type: type,
                                                    }))
                                                }
                                            >
                                                {type === 'exchange'
                                                    ? labels.paymentTypeExchange
                                                    : labels.paymentTypeGeneral}
                                            </Button>
                                        ))}
                                    </div>
                                    <p className="text-muted-foreground text-sm">
                                        {form.data.payment_type === 'exchange'
                                            ? labels.paymentTypeExchangeHint
                                            : labels.paymentTypeGeneralHint}
                                    </p>
                                </div>

                                {form.data.payment_type === 'exchange' ? (
                                    <div className="space-y-4 rounded-xl border border-border p-4">
                                        <div className="space-y-2">
                                            <Label htmlFor="saraf-payment-exchange">
                                                {labels.exchangeLabel}
                                            </Label>
                                            <Select
                                                value={form.data.currency_exchange_id}
                                                onValueChange={(value) =>
                                                    form.setData('currency_exchange_id', value)
                                                }
                                            >
                                                <SelectTrigger
                                                    id="saraf-payment-exchange"
                                                    className={cn(
                                                        form.errors.currency_exchange_id &&
                                                            'border-destructive',
                                                    )}
                                                >
                                                    <SelectValue placeholder={labels.exchangePlaceholder} />
                                                </SelectTrigger>
                                                <SelectContent>
                                                    {openExchanges.map((exchange) => (
                                                        <SelectItem
                                                            key={exchange.id}
                                                            value={String(exchange.id)}
                                                        >
                                                            {labels.exchangeOption
                                                                .replace(
                                                                    '{date}',
                                                                    formatAppDateOrDash(
                                                                        exchange.exchange_date,
                                                                        locale,
                                                                    ),
                                                                )
                                                                .replace(
                                                                    '{remain}',
                                                                    formatMoneyAmountOrDash(
                                                                        exchange.due_amount ??
                                                                            exchange.remain_dollar_on_saraf,
                                                                        locale,
                                                                    ),
                                                                )}
                                                        </SelectItem>
                                                    ))}
                                                </SelectContent>
                                            </Select>
                                            {form.errors.currency_exchange_id ? (
                                                <p className="text-destructive text-sm">
                                                    {form.errors.currency_exchange_id}
                                                </p>
                                            ) : null}
                                        </div>

                                        {selectedExchange ? (
                                            <div className="rounded-lg border border-dashed bg-muted/20 px-3 py-2 text-sm">
                                                <span className="text-muted-foreground">
                                                    {labels.exchangeRemainLabel}:{' '}
                                                </span>
                                                <span className="font-semibold tabular-nums">
                                                    {formatMoneyAmountOrDash(
                                                        selectedExchange.due_amount ??
                                                            selectedExchange.remain_dollar_on_saraf,
                                                        locale,
                                                    )}{' '}
                                                    {labels.usdCurrencyLabel}
                                                </span>
                                            </div>
                                        ) : null}
                                    </div>
                                ) : (
                                    <div className="space-y-4 rounded-xl border border-border p-4">
                                        <div>
                                            <h3 className="text-sm font-semibold">{labels.fifoSection}</h3>
                                            <p className="text-muted-foreground text-sm">
                                                {labels.fifoSectionHint}
                                            </p>
                                        </div>
                                        {fifoPreview?.allocations?.length ? (
                                            <ul className="space-y-2 text-sm">
                                                {fifoPreview.allocations.map((row, index) => (
                                                    <li
                                                        key={row.bill.id}
                                                        className="flex items-center justify-between gap-3 rounded-lg border bg-muted/15 px-3 py-2"
                                                    >
                                                        <span>
                                                            {labels.fifoOrderLabel.replace(
                                                                '{order}',
                                                                String(index + 1),
                                                            )}
                                                            {' — '}
                                                            {formatAppDateOrDash(
                                                                row.bill.exchange_date,
                                                                locale,
                                                            )}
                                                        </span>
                                                        <span className="font-medium tabular-nums">
                                                            {formatMoneyAmountOrDash(row.amount, locale)}{' '}
                                                            {labels.usdCurrencyLabel}
                                                        </span>
                                                    </li>
                                                ))}
                                            </ul>
                                        ) : (
                                            <p className="text-muted-foreground text-sm">
                                                {labels.fifoPreviewEmpty}
                                            </p>
                                        )}
                                    </div>
                                )}

                                <div className="grid gap-4 sm:grid-cols-2">
                                    <div className="space-y-2">
                                        <Label htmlFor="saraf-payment-amount">{labels.amountLabel}</Label>
                                        <Input
                                            id="saraf-payment-amount"
                                            type="text"
                                            inputMode="decimal"
                                            value={form.data.amount}
                                            onChange={(e) =>
                                                form.setData(
                                                    'amount',
                                                    sanitizeDecimalInput(e.target.value, {
                                                        maxDecimals: 2,
                                                    }),
                                                )
                                            }
                                            placeholder={labels.amountPlaceholder}
                                            aria-invalid={Boolean(form.errors.amount)}
                                            className={cn(
                                                'tabular-nums',
                                                form.errors.amount && 'border-destructive',
                                            )}
                                        />
                                        {maxAmount != null && maxAmount > 0 ? (
                                            <p className="text-muted-foreground text-xs">
                                                {labels.maxAmountHint.replace(
                                                    '{amount}',
                                                    formatMoneyAmountOrDash(maxAmount, locale),
                                                )}
                                            </p>
                                        ) : null}
                                        {form.errors.amount ? (
                                            <p className="text-destructive text-sm">{form.errors.amount}</p>
                                        ) : null}
                                    </div>

                                    <div className="space-y-2">
                                        <Label htmlFor="saraf-payment-date">{labels.paidAtLabel}</Label>
                                        <AppDateInput
                                            id="saraf-payment-date"
                                            value={form.data.paid_at}
                                            onChange={(value) => form.setData('paid_at', value)}
                                            invalid={Boolean(form.errors.paid_at)}
                                        />
                                        {form.errors.paid_at ? (
                                            <p className="text-destructive text-sm">{form.errors.paid_at}</p>
                                        ) : null}
                                    </div>

                                    <div className="space-y-2">
                                        <Label htmlFor="saraf-payment-method">{labels.paymentMethodLabel}</Label>
                                        <Select
                                            value={form.data.payment_method}
                                            onValueChange={(value) => form.setData('payment_method', value)}
                                        >
                                            <SelectTrigger id="saraf-payment-method">
                                                <SelectValue />
                                            </SelectTrigger>
                                            <SelectContent>
                                                {paymentMethods.map((method) => (
                                                    <SelectItem key={method} value={method}>
                                                        {paymentMethodLabel(method, labels)}
                                                    </SelectItem>
                                                ))}
                                            </SelectContent>
                                        </Select>
                                    </div>

                                    <div className="space-y-2">
                                        <Label htmlFor="saraf-payment-reference">{labels.referenceLabel}</Label>
                                        <Input
                                            id="saraf-payment-reference"
                                            value={form.data.reference}
                                            onChange={(e) => form.setData('reference', e.target.value)}
                                            placeholder={labels.referencePlaceholder}
                                        />
                                    </div>

                                    <div className="space-y-2 sm:col-span-2">
                                        <Label htmlFor="saraf-payment-notes">{labels.notesLabel}</Label>
                                        <Input
                                            id="saraf-payment-notes"
                                            value={form.data.notes}
                                            onChange={(e) => form.setData('notes', e.target.value)}
                                            placeholder={labels.notesPlaceholder}
                                        />
                                    </div>
                                </div>

                                <div className="flex justify-end">
                                    <Button type="submit" disabled={!canSubmit} className="min-w-40">
                                        {form.processing ? (
                                            <Spinner className="size-4" />
                                        ) : (
                                            <Save className="size-4" />
                                        )}
                                        {form.processing ? labels.savingPayment : labels.savePayment}
                                    </Button>
                                </div>
                            </>
                        )}
                    </form>
                </CardContent>
            </Card>
        </>
    );
}
