import { Link, router, usePage } from '@inertiajs/react';
import { Save } from 'lucide-react';
import { useEffect, useMemo, useState } from 'react';
import { toast } from 'sonner';
import { exchangePacks as exchangeSale } from '@/actions/App/Http/Controllers/SaleController';
import { ConfirmActionDialog } from '@/components/confirm-action-dialog';
import { SaleExchangeIncomingTable } from '@/components/sales/sale-exchange-incoming-table';
import { SaleExchangeOutgoingLines } from '@/components/sales/sale-exchange-outgoing-lines';
import { SaleExchangeSummary } from '@/components/sales/sale-exchange-summary';
import { Button } from '@/components/ui/button';
import {
    Card,
    CardContent,
    CardDescription,
    CardFooter,
    CardHeader,
    CardTitle,
} from '@/components/ui/card';
import { Input } from '@/components/ui/input';
import { Label } from '@/components/ui/label';
import { Textarea } from '@/components/ui/textarea';
import { formatQuantityForInput } from '@/lib/dates';
import { firstInertiaErrorMessage } from '@/lib/inertia-errors';
import {
    initialSaleExchangeOutgoingLines,
    saleExchangeOutgoingLinesPayload,
    validateSaleExchangeOutgoingLines,
} from '@/lib/sale-exchange-outgoing-lines';
import {
    createEmptySaleExchangeIncomingEntry,
    initialSaleExchangeEntries,
    saleExchangeIncomingQuantityByProduct,
    saleExchangeLineKey,
    saleExchangeReturnLinesPayloadFromEntries,
    saleExchangeTotalsFromEntries,
    validateSaleExchangeIncomingEntries,
} from '@/lib/sale-exchange-quantities';
import {
    paginateSaleReturnLines,
    SALE_RETURN_COMPACT_THRESHOLD,
    SALE_RETURN_PAGE_SIZE,
    saleReturnLineMatchesSearch,
} from '@/lib/sale-return-list';
import { saleReturnWeightForQuantity } from '@/lib/sale-return-quantities';
import { cn } from '@/lib/utils';
import { zodErrorToNestedFieldMap } from '@/lib/zod-errors';
import { buildExchangeSalePacksSchema } from '@/validation/schemas';

/**
 * @param {object} props
 * @param {Record<string, string>} props.labels
 * @param {Record<string, string>} props.validation
 * @param {Record<string, string>} props.appActions
 * @param {{ id: number, sale_number?: string, customer_name?: string, order_number?: string|null }} props.sale
 * @param {Array<{id: number, name: string, unit_name?: string|null}>} [props.products]
 * @param {string} [props.cancelHref]
 * @param {string} props.locale
 */
export function SaleExchangeForm({
    labels,
    validation,
    appActions,
    sale,
    products = [],
    cancelHref,
    locale,
}) {
    const pageProps = usePage().props;
    const pageErrors = pageProps.errors ?? {};
    const lines = Array.isArray(pageProps.lines) ? pageProps.lines : [];
    const usePagedProducts = lines.length >= SALE_RETURN_COMPACT_THRESHOLD;

    const [entries, setEntries] = useState(() =>
        initialSaleExchangeEntries(lines),
    );
    const [outgoingLines, setOutgoingLines] = useState(() =>
        initialSaleExchangeOutgoingLines(),
    );
    const [notes, setNotes] = useState('');
    const [errors, setErrors] = useState({});
    const [processing, setProcessing] = useState(false);
    const [search, setSearch] = useState('');
    const [page, setPage] = useState(1);
    const [confirmOpen, setConfirmOpen] = useState(false);
    const saleError =
        typeof pageErrors.sale === 'string'
            ? pageErrors.sale
            : Array.isArray(pageErrors.sale)
              ? pageErrors.sale[0]
              : '';

    const schema = useMemo(
        () => buildExchangeSalePacksSchema(validation),
        [validation],
    );

    const exchangeTotals = useMemo(
        () => saleExchangeTotalsFromEntries(lines, entries, outgoingLines),
        [lines, entries, outgoingLines],
    );

    const incomingByProduct = useMemo(
        () => saleExchangeIncomingQuantityByProduct(lines, entries),
        [lines, entries],
    );

    const incomingProductIds = useMemo(() => {
        const ids = new Set();

        for (const line of lines) {
            const entry =
                entries[saleExchangeLineKey(line)] ??
                createEmptySaleExchangeIncomingEntry();

            if ((entry.quantity ?? 0) > 0 && line.factory_product_id != null) {
                ids.add(String(line.factory_product_id));
            }
        }

        return ids;
    }, [lines, entries]);

    const soldProductIds = useMemo(
        () =>
            new Set(
                lines
                    .map((line) =>
                        line.factory_product_id != null
                            ? String(line.factory_product_id)
                            : '',
                    )
                    .filter(Boolean),
            ),
        [lines],
    );

    const outgoingProducts = useMemo(() => {
        const excludeIncoming =
            incomingProductIds.size > 0 &&
            [...soldProductIds].some((id) => !incomingProductIds.has(id));

        if (!excludeIncoming) {
            return products;
        }

        return products.filter(
            (product) => !incomingProductIds.has(String(product.id)),
        );
    }, [products, incomingProductIds, soldProductIds]);

    const hasIncomingSelection = exchangeTotals.quantity > 0;

    const filteredLines = useMemo(
        () =>
            lines.filter((line) =>
                saleReturnLineMatchesSearch(line.product_name, search),
            ),
        [lines, search],
    );

    const pagination = useMemo(
        () =>
            usePagedProducts
                ? paginateSaleReturnLines(
                      filteredLines,
                      page,
                      SALE_RETURN_PAGE_SIZE,
                  )
                : {
                      items: filteredLines,
                      currentPage: 1,
                      totalPages: 1,
                      totalItems: filteredLines.length,
                      from: filteredLines.length > 0 ? 1 : 0,
                      to: filteredLines.length,
                  },
        [usePagedProducts, filteredLines, page],
    );

    useEffect(() => {
        setPage(1);
    }, [search]);

    useEffect(() => {
        if (page > pagination.totalPages) {
            setPage(pagination.totalPages);
        }
    }, [page, pagination.totalPages]);

    useEffect(() => {
        if (saleError) {
            toast.error(saleError);
        }
    }, [saleError]);

    const allExchanged = lines.every((line) => {
        const entry =
            entries[saleExchangeLineKey(line)] ??
            createEmptySaleExchangeIncomingEntry();

        return (entry.quantity ?? 0) === line.quantity;
    });

    function updateEntry(line, field, value) {
        const key = saleExchangeLineKey(line);

        setEntries((current) => {
            const next = { ...current };

            if (
                field === 'quantity' &&
                Number.parseInt(String(value), 10) > 0
            ) {
                for (const otherLine of lines) {
                    const otherKey = saleExchangeLineKey(otherLine);

                    if (otherKey === key) {
                        continue;
                    }

                    next[otherKey] = createEmptySaleExchangeIncomingEntry();
                }
            }

            next[key] = {
                ...(next[key] ?? createEmptySaleExchangeIncomingEntry()),
                [field]: value,
            };

            return next;
        });
    }

    function exchangeAllLines() {
        if (lines.length === 0) {
            return;
        }

        const line = lines[0];
        const key = saleExchangeLineKey(line);
        const defaultReturnWeight = formatQuantityForInput(
            saleReturnWeightForQuantity(line, line.quantity),
        );

        setEntries(
            Object.fromEntries(
                lines.map((entryLine) => {
                    const entryKey = saleExchangeLineKey(entryLine);

                    if (entryKey !== key) {
                        return [
                            entryKey,
                            createEmptySaleExchangeIncomingEntry(),
                        ];
                    }

                    return [
                        entryKey,
                        {
                            ...createEmptySaleExchangeIncomingEntry(),
                            quantity: line.quantity,
                            return_weight: defaultReturnWeight,
                            weight_price:
                                line.weight_price != null &&
                                line.weight_price !== ''
                                    ? String(line.weight_price)
                                    : '',
                        },
                    ];
                }),
            ),
        );
    }

    function clearAllExchanges() {
        setEntries(initialSaleExchangeEntries(lines));
    }

    function buildValidatedPayload() {
        setErrors({});

        const incomingError = validateSaleExchangeIncomingEntries(
            lines,
            entries,
        );
        const outgoingError = validateSaleExchangeOutgoingLines(
            outgoingLines,
            products,
            incomingByProduct,
        );
        const validationError = incomingError ?? outgoingError;

        if (validationError) {
            const message =
                labels[validationError] ??
                validation.required ??
                appActions.requestFailed;
            setErrors({ exchange: message });
            toast.error(message);
            return null;
        }

        const returnLines = saleExchangeReturnLinesPayloadFromEntries(
            lines,
            entries,
        );
        const replacementLines =
            saleExchangeOutgoingLinesPayload(outgoingLines);

        const parsed = schema.safeParse({
            return_lines: returnLines,
            replacement_lines: replacementLines,
            notes,
        });

        if (!parsed.success) {
            setErrors(zodErrorToNestedFieldMap(parsed.error));
            toast.error(
                parsed.error.flatten().fieldErrors.return_lines?.[0] ??
                    parsed.error.flatten().fieldErrors.replacement_lines?.[0] ??
                    labels.selectProductsToExchange ??
                    validation.required ??
                    appActions.requestFailed,
            );
            return null;
        }

        return parsed.data;
    }

    function handleSubmit(e) {
        e.preventDefault();
        if (!buildValidatedPayload()) {
            return;
        }
        setConfirmOpen(true);
    }

    function commitSave() {
        const data = buildValidatedPayload();
        if (!data) {
            setConfirmOpen(false);
            return;
        }

        setProcessing(true);
        router.post(
            exchangeSale.url(sale.id),
            {
                return_lines: data.return_lines,
                replacement_lines: data.replacement_lines,
                notes: data.notes || undefined,
            },
            {
                onError: (pageErrors) =>
                    toast.error(
                        firstInertiaErrorMessage(
                            pageErrors,
                            appActions.requestFailed,
                        ),
                    ),
                onFinish: () => setProcessing(false),
            },
        );
    }

    const canSubmit =
        hasIncomingSelection &&
        (exchangeTotals.outgoingQuantity ?? 0) > 0 &&
        !processing;

    return (
        <>
        <ConfirmActionDialog
            open={confirmOpen}
            onOpenChange={setConfirmOpen}
            title={labels.confirmExchangeTitle}
            description={labels.confirmExchangeBody}
            confirmLabel={labels.confirmExchange}
            cancelLabel={appActions.cancel}
            processing={processing}
            onConfirm={commitSave}
        />
        <form onSubmit={handleSubmit} className="mx-auto w-full max-w-7xl">
            <Card>
                <CardHeader>
                    <CardTitle>{labels.exchangeHeadTitle}</CardTitle>
                    <CardDescription>
                        {labels.exchangeDescription}
                    </CardDescription>
                </CardHeader>

                <CardContent className="space-y-8">
                    {saleError ? (
                        <p className="rounded-md border border-destructive/30 bg-destructive/5 px-4 py-3 text-sm text-destructive">
                            {saleError}
                        </p>
                    ) : null}

                    <section className="space-y-4">
                        <h3 className="text-sm font-semibold">
                            {labels.billDetailsSection}
                        </h3>

                        <div className="grid gap-4 sm:grid-cols-2">
                            <div className="space-y-2">
                                <Label>{labels.billNoLabel}</Label>
                                <Input
                                    value={sale.sale_number ?? '—'}
                                    disabled
                                    className="bg-muted/40 font-mono text-sm"
                                />
                            </div>
                            <div className="space-y-2">
                                <Label>{labels.customerLabel}</Label>
                                <Input
                                    value={sale.customer_name ?? '—'}
                                    disabled
                                />
                            </div>
                        </div>
                    </section>

                    <section className="space-y-4">
                        <div className="flex flex-col gap-3 sm:flex-row sm:items-start sm:justify-between">
                            <div>
                                <h3 className="text-sm font-semibold">
                                    {labels.exchangeIncomingSectionTitle}
                                </h3>
                                <p className="mt-1 text-sm text-muted-foreground">
                                    {labels.exchangeIncomingSingleHint}
                                </p>
                            </div>
                            <div className="flex flex-wrap gap-2">
                                <Button
                                    type="button"
                                    variant="outline"
                                    size="sm"
                                    onClick={exchangeAllLines}
                                    disabled={
                                        allExchanged || lines.length === 0
                                    }
                                >
                                    {labels.exchangeAllProducts}
                                </Button>
                                <Button
                                    type="button"
                                    variant="outline"
                                    size="sm"
                                    onClick={clearAllExchanges}
                                    disabled={exchangeTotals.quantity === 0}
                                >
                                    {labels.clearExchangeQuantities}
                                </Button>
                            </div>
                        </div>

                        {usePagedProducts ? (
                            <div className="flex flex-col gap-3 sm:flex-row sm:items-end sm:justify-between">
                                <div className="max-w-md flex-1 space-y-2">
                                    <Label htmlFor="exchange_product_search">
                                        {labels.exchangeProductSearchLabel}
                                    </Label>
                                    <Input
                                        id="exchange_product_search"
                                        type="search"
                                        value={search}
                                        onChange={(e) =>
                                            setSearch(e.target.value)
                                        }
                                        placeholder={
                                            labels.exchangeProductSearchPlaceholder
                                        }
                                        className="h-9"
                                    />
                                </div>
                                <p className="text-sm text-muted-foreground tabular-nums">
                                    {labels.exchangeProductCountLabel
                                        .replace(
                                            '{shown}',
                                            String(pagination.totalItems),
                                        )
                                        .replace(
                                            '{total}',
                                            String(lines.length),
                                        )}
                                </p>
                            </div>
                        ) : null}

                        {lines.length > 0 && pagination.items.length === 0 ? (
                            <div className="rounded-xl border border-dashed border-border px-4 py-10 text-center text-sm text-muted-foreground">
                                {labels.exchangeProductSearchEmpty}
                            </div>
                        ) : (
                            <SaleExchangeIncomingTable
                                lines={pagination.items}
                                entries={entries}
                                labels={labels}
                                locale={locale}
                                onUpdate={updateEntry}
                            />
                        )}

                        {usePagedProducts &&
                        pagination.totalItems > SALE_RETURN_PAGE_SIZE ? (
                            <div className="flex flex-col gap-3 border-t border-border pt-4 sm:flex-row sm:items-center sm:justify-between">
                                <p className="text-sm text-muted-foreground tabular-nums">
                                    {labels.returnPaginationSummary
                                        .replace(
                                            '{from}',
                                            String(pagination.from),
                                        )
                                        .replace('{to}', String(pagination.to))
                                        .replace(
                                            '{total}',
                                            String(pagination.totalItems),
                                        )}
                                </p>
                                <div className="flex items-center gap-2">
                                    <Button
                                        type="button"
                                        variant="outline"
                                        size="sm"
                                        onClick={() =>
                                            setPage((current) =>
                                                Math.max(1, current - 1),
                                            )
                                        }
                                        disabled={pagination.currentPage <= 1}
                                    >
                                        {labels.returnPaginationPrevious}
                                    </Button>
                                    <span className="min-w-[7rem] text-center text-sm text-muted-foreground tabular-nums">
                                        {labels.returnPaginationPages
                                            .replace(
                                                '{page}',
                                                String(pagination.currentPage),
                                            )
                                            .replace(
                                                '{pages}',
                                                String(pagination.totalPages),
                                            )}
                                    </span>
                                    <Button
                                        type="button"
                                        variant="outline"
                                        size="sm"
                                        onClick={() =>
                                            setPage((current) =>
                                                Math.min(
                                                    pagination.totalPages,
                                                    current + 1,
                                                ),
                                            )
                                        }
                                        disabled={
                                            pagination.currentPage >=
                                            pagination.totalPages
                                        }
                                    >
                                        {labels.returnPaginationNext}
                                    </Button>
                                </div>
                            </div>
                        ) : null}
                    </section>

                    <SaleExchangeOutgoingLines
                        lines={outgoingLines}
                        onLinesChange={setOutgoingLines}
                        products={outgoingProducts}
                        incomingByProduct={incomingByProduct}
                        labels={labels}
                        locale={locale}
                        disabled={!hasIncomingSelection}
                    />

                    {hasIncomingSelection ? (
                        <SaleExchangeSummary
                            labels={labels}
                            locale={locale}
                            totals={exchangeTotals}
                            lines={lines}
                            entries={entries}
                            outgoingLines={outgoingLines}
                            products={outgoingProducts}
                        />
                    ) : null}

                    {errors.exchange ? (
                        <p className="text-sm text-destructive">
                            {errors.exchange}
                        </p>
                    ) : null}

                    <section className="space-y-2">
                        <Label htmlFor="exchange_notes">
                            {labels.notesLabel}
                        </Label>
                        <Textarea
                            id="exchange_notes"
                            value={notes}
                            onChange={(e) => setNotes(e.target.value)}
                            placeholder={labels.exchangeNotesPlaceholder}
                            rows={3}
                            className={cn(errors.notes && 'border-destructive')}
                        />
                        {errors.notes ? (
                            <p className="text-sm text-destructive">
                                {errors.notes}
                            </p>
                        ) : null}
                    </section>
                </CardContent>

                <CardFooter className="flex flex-col-reverse gap-3 border-t border-border pt-6 sm:flex-row sm:justify-end">
                    {cancelHref ? (
                        <Button
                            type="button"
                            variant="outline"
                            asChild
                            disabled={processing}
                        >
                            <Link href={cancelHref}>{appActions.cancel}</Link>
                        </Button>
                    ) : null}
                    <Button type="submit" disabled={!canSubmit}>
                        <Save className="size-4" />
                        {processing
                            ? labels.exchangingProducts
                            : labels.confirmExchange}
                    </Button>
                </CardFooter>
            </Card>
        </form>
        </>
    );
}
