import { Head, router } from '@inertiajs/react';
import { Lock, Unlock, Wallet } from 'lucide-react';
import { useEffect, useState } from 'react';
import { toast } from 'sonner';
import CashierController from '@/actions/App/Http/Controllers/CashierController';
import { Badge } from '@/components/ui/badge';
import { Button } from '@/components/ui/button';
import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card';
import {
    Dialog,
    DialogContent,
    DialogDescription,
    DialogFooter,
    DialogHeader,
    DialogTitle,
    DialogTrigger,
} from '@/components/ui/dialog';
import { Input } from '@/components/ui/input';
import { Label } from '@/components/ui/label';
import {
    Select,
    SelectContent,
    SelectItem,
    SelectTrigger,
    SelectValue,
} from '@/components/ui/select';
import SubmitSpinner from '@/components/submit-spinner';

const priceFormatter = new Intl.NumberFormat('fr-FR');

function formatFcfa(amount: number): string {
    return `${priceFormatter.format(amount)} FCFA`;
}

type PaymentMethod =
    | 'cash'
    | 'card'
    | 'orange_money'
    | 'mtn_momo'
    | 'moov_money'
    | 'wave';

const methodLabels: Record<PaymentMethod, string> = {
    cash: 'Espèces',
    card: 'Carte bancaire',
    orange_money: 'Orange Money',
    mtn_momo: 'MTN MoMo',
    moov_money: 'Moov Money',
    wave: 'Wave',
};

type Payment = {
    id: number;
    method: PaymentMethod;
    amount: number;
};

type Session = {
    id: number;
    opening_amount: number;
    status: 'open' | 'closed';
    opened_at: string;
    payments: Payment[];
};

type OrderItem = {
    id: number;
    quantity: number;
    unit_price: number;
    product: { id: number; name: string };
};

type PendingOrder = {
    id: number;
    type: 'dine_in' | 'takeaway' | 'phone';
    status: string;
    total: number;
    table: { id: number; number: string } | null;
    items: OrderItem[];
    payments: Payment[];
};

type Props = {
    session: Session | null;
    pendingOrders: PendingOrder[];
};

const typeLabels: Record<PendingOrder['type'], string> = {
    dine_in: 'Sur place',
    takeaway: 'À emporter',
    phone: 'Téléphone / WhatsApp',
};

function amountPaid(order: PendingOrder): number {
    return order.payments.reduce((sum, p) => sum + p.amount, 0);
}

function balanceDue(order: PendingOrder): number {
    return Math.max(0, order.total - amountPaid(order));
}

export default function CashierIndex({ session, pendingOrders }: Props) {
    const [opening, setOpening] = useState(false);
    const [closing, setClosing] = useState(false);
    const [payingOrder, setPayingOrder] = useState<PendingOrder | null>(null);

    return (
        <>
            <Head title="Caisse" />

            <div className="flex flex-1 flex-col gap-6 p-4">
                <div className="flex items-center justify-between">
                    <div>
                        <h1 className="text-xl font-semibold tracking-tight">
                            Caisse
                        </h1>
                        <p className="text-muted-foreground text-sm">
                            Encaissement des commandes et suivi de la session
                            de caisse.
                        </p>
                    </div>

                    {session ? (
                        <Button
                            variant="outline"
                            onClick={() => setClosing(true)}
                        >
                            <Lock />
                            Clôturer la session
                        </Button>
                    ) : (
                        <Button onClick={() => setOpening(true)}>
                            <Unlock />
                            Ouvrir une session
                        </Button>
                    )}
                </div>

                <Card>
                    <CardHeader>
                        <CardTitle className="text-base">
                            Session de caisse
                        </CardTitle>
                    </CardHeader>
                    <CardContent>
                        {session ? (
                            <div className="grid gap-2 text-sm sm:grid-cols-3">
                                <div>
                                    <p className="text-muted-foreground">
                                        Fond de caisse initial
                                    </p>
                                    <p className="font-medium">
                                        {formatFcfa(session.opening_amount)}
                                    </p>
                                </div>
                                <div>
                                    <p className="text-muted-foreground">
                                        Encaissé cette session
                                    </p>
                                    <p className="font-medium">
                                        {formatFcfa(
                                            session.payments.reduce(
                                                (sum, p) => sum + p.amount,
                                                0,
                                            ),
                                        )}
                                    </p>
                                </div>
                                <div>
                                    <p className="text-muted-foreground">
                                        Statut
                                    </p>
                                    <Badge variant="default">Ouverte</Badge>
                                </div>
                            </div>
                        ) : (
                            <p className="text-muted-foreground text-sm">
                                Aucune session de caisse ouverte. Ouvrez une
                                session pour commencer à encaisser.
                            </p>
                        )}
                    </CardContent>
                </Card>

                <div>
                    <h2 className="mb-3 text-sm font-medium tracking-tight">
                        Commandes en attente d'encaissement
                    </h2>

                    {pendingOrders.length === 0 ? (
                        <p className="text-muted-foreground text-sm">
                            Aucune commande en attente de paiement.
                        </p>
                    ) : (
                        <div className="grid gap-4 lg:grid-cols-2 xl:grid-cols-3">
                            {pendingOrders.map((order) => (
                                <Card key={order.id}>
                                    <CardHeader className="flex-row items-start justify-between">
                                        <div>
                                            <CardTitle className="text-base">
                                                Commande #{order.id}
                                            </CardTitle>
                                            <p className="text-muted-foreground text-xs">
                                                {typeLabels[order.type]}
                                                {order.table &&
                                                    ` · Table ${order.table.number}`}
                                            </p>
                                        </div>
                                        {amountPaid(order) > 0 && (
                                            <Badge variant="secondary">
                                                Partiellement payée
                                            </Badge>
                                        )}
                                    </CardHeader>
                                    <CardContent className="space-y-3">
                                        <ul className="space-y-1 text-sm">
                                            {order.items.map((item) => (
                                                <li
                                                    key={item.id}
                                                    className="flex justify-between"
                                                >
                                                    <span>
                                                        {item.quantity}×{' '}
                                                        {item.product.name}
                                                    </span>
                                                    <span className="text-muted-foreground">
                                                        {formatFcfa(
                                                            item.quantity *
                                                                item.unit_price,
                                                        )}
                                                    </span>
                                                </li>
                                            ))}
                                        </ul>

                                        <div className="space-y-1 border-t pt-2 text-sm">
                                            <div className="flex items-center justify-between">
                                                <span>Total</span>
                                                <span>
                                                    {formatFcfa(order.total)}
                                                </span>
                                            </div>
                                            {amountPaid(order) > 0 && (
                                                <div className="text-muted-foreground flex items-center justify-between">
                                                    <span>Déjà payé</span>
                                                    <span>
                                                        {formatFcfa(
                                                            amountPaid(order),
                                                        )}
                                                    </span>
                                                </div>
                                            )}
                                            <div className="flex items-center justify-between font-medium">
                                                <span>Reste à payer</span>
                                                <span>
                                                    {formatFcfa(
                                                        balanceDue(order),
                                                    )}
                                                </span>
                                            </div>
                                        </div>

                                        <Button
                                            size="sm"
                                            className="w-full"
                                            disabled={!session}
                                            onClick={() =>
                                                setPayingOrder(order)
                                            }
                                        >
                                            <Wallet />
                                            Encaisser
                                        </Button>
                                    </CardContent>
                                </Card>
                            ))}
                        </div>
                    )}
                </div>
            </div>

            <OpenSessionDialog open={opening} onOpenChange={setOpening} />

            {session && (
                <CloseSessionDialog
                    open={closing}
                    onOpenChange={setClosing}
                    sessionId={session.id}
                    expectedHint={
                        session.opening_amount +
                        session.payments
                            .filter((p) => p.method === 'cash')
                            .reduce((sum, p) => sum + p.amount, 0)
                    }
                />
            )}

            <CollectPaymentDialog
                order={payingOrder}
                onOpenChange={(open) => !open && setPayingOrder(null)}
            />
        </>
    );
}

function OpenSessionDialog({
    open,
    onOpenChange,
}: {
    open: boolean;
    onOpenChange: (open: boolean) => void;
}) {
    const [openingAmount, setOpeningAmount] = useState('0');
    const [processing, setProcessing] = useState(false);

    const submit = () => {
        setProcessing(true);

        router.post(
            CashierController.openSession.url(),
            { opening_amount: Number(openingAmount) },
            {
                preserveScroll: true,
                onSuccess: () => onOpenChange(false),
                onError: (errors) =>
                    toast.error(
                        errors.domain ?? "Impossible d'ouvrir la session.",
                    ),
                onFinish: () => setProcessing(false),
            },
        );
    };

    return (
        <Dialog open={open} onOpenChange={onOpenChange}>
            <DialogContent className="sm:max-w-sm">
                <DialogHeader>
                    <DialogTitle>Ouvrir une session de caisse</DialogTitle>
                    <DialogDescription>
                        Indiquez le fond de caisse initial en espèces.
                    </DialogDescription>
                </DialogHeader>

                <div className="grid gap-2 py-2">
                    <Label htmlFor="opening_amount">
                        Fond de caisse initial (FCFA)
                    </Label>
                    <Input
                        id="opening_amount"
                        type="number"
                        min={0}
                        value={openingAmount}
                        onChange={(e) => setOpeningAmount(e.target.value)}
                    />
                </div>

                <DialogFooter>
                    <Button onClick={submit} disabled={processing}>
                        <SubmitSpinner show={processing} />
                        Ouvrir
                    </Button>
                </DialogFooter>
            </DialogContent>
        </Dialog>
    );
}

function CloseSessionDialog({
    open,
    onOpenChange,
    sessionId,
    expectedHint,
}: {
    open: boolean;
    onOpenChange: (open: boolean) => void;
    sessionId: number;
    expectedHint: number;
}) {
    const [closingAmount, setClosingAmount] = useState('');
    const [processing, setProcessing] = useState(false);

    const submit = () => {
        setProcessing(true);

        router.post(
            CashierController.closeSession.url(sessionId),
            { closing_amount: Number(closingAmount) },
            {
                preserveScroll: true,
                onSuccess: () => onOpenChange(false),
                onError: (errors) =>
                    toast.error(
                        errors.domain ?? 'Impossible de clôturer la session.',
                    ),
                onFinish: () => setProcessing(false),
            },
        );
    };

    return (
        <Dialog open={open} onOpenChange={onOpenChange}>
            <DialogContent className="sm:max-w-sm">
                <DialogHeader>
                    <DialogTitle>Clôturer la session de caisse</DialogTitle>
                    <DialogDescription>
                        Comptez les espèces en caisse et saisissez le montant
                        constaté. Montant espèces attendu :{' '}
                        {formatFcfa(expectedHint)}.
                    </DialogDescription>
                </DialogHeader>

                <div className="grid gap-2 py-2">
                    <Label htmlFor="closing_amount">
                        Montant compté (FCFA)
                    </Label>
                    <Input
                        id="closing_amount"
                        type="number"
                        min={0}
                        value={closingAmount}
                        onChange={(e) => setClosingAmount(e.target.value)}
                    />
                </div>

                <DialogFooter>
                    <Button
                        onClick={submit}
                        disabled={processing || closingAmount === ''}
                    >
                        <SubmitSpinner show={processing} />
                        Clôturer
                    </Button>
                </DialogFooter>
            </DialogContent>
        </Dialog>
    );
}

function CollectPaymentDialog({
    order,
    onOpenChange,
}: {
    order: PendingOrder | null;
    onOpenChange: (open: boolean) => void;
}) {
    const [method, setMethod] = useState<PaymentMethod>('cash');
    const [amount, setAmount] = useState('');
    const [processing, setProcessing] = useState(false);

    useEffect(() => {
        if (order) {
            setAmount(String(balanceDue(order)));
        }
        // eslint-disable-next-line react-hooks/exhaustive-deps
    }, [order?.id]);

    const submit = () => {
        if (!order) return;
        setProcessing(true);

        router.post(
            CashierController.collectPayment.url(order.id),
            { method, amount: Number(amount) },
            {
                preserveScroll: true,
                onSuccess: () => {
                    onOpenChange(false);
                    setAmount('');
                    setMethod('cash');
                },
                onError: (errors) =>
                    toast.error(
                        errors.domain ?? "Impossible d'encaisser ce paiement.",
                    ),
                onFinish: () => setProcessing(false),
            },
        );
    };

    return (
        <Dialog
            open={order !== null}
            onOpenChange={(open) => {
                if (!open) setAmount('');
                onOpenChange(open);
            }}
        >
            <DialogContent className="sm:max-w-sm">
                <DialogHeader>
                    <DialogTitle>
                        Encaisser la commande #{order?.id}
                    </DialogTitle>
                    <DialogDescription>
                        {order &&
                            `Reste à payer : ${formatFcfa(balanceDue(order))}.`}
                    </DialogDescription>
                </DialogHeader>

                <div className="grid gap-4 py-2">
                    <div className="grid gap-2">
                        <Label htmlFor="method">Moyen de paiement</Label>
                        <Select
                            value={method}
                            onValueChange={(value) =>
                                setMethod(value as PaymentMethod)
                            }
                        >
                            <SelectTrigger id="method" className="w-full">
                                <SelectValue />
                            </SelectTrigger>
                            <SelectContent>
                                {Object.entries(methodLabels).map(
                                    ([value, label]) => (
                                        <SelectItem key={value} value={value}>
                                            {label}
                                        </SelectItem>
                                    ),
                                )}
                            </SelectContent>
                        </Select>
                    </div>

                    <div className="grid gap-2">
                        <Label htmlFor="amount">Montant (FCFA)</Label>
                        <Input
                            id="amount"
                            type="number"
                            min={1}
                            value={amount}
                            onChange={(e) => setAmount(e.target.value)}
                        />
                    </div>
                </div>

                <DialogFooter>
                    <Button
                        onClick={submit}
                        disabled={processing || amount === ''}
                    >
                        <SubmitSpinner show={processing} />
                        Encaisser
                    </Button>
                </DialogFooter>
            </DialogContent>
        </Dialog>
    );
}
