import { useForm } from '@inertiajs/react';
import { Package, Plus, Trash2 } from 'lucide-react';
import type { FormEvent } from 'react';
import InvoiceController from '@/actions/App/Http/Controllers/Admin/InvoiceController';
import InputError from '@/components/input-error';
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 { Textarea } from '@/components/ui/textarea';
import { useCompanyCurrency } from '@/hooks/use-company-currency';
import { useCompanySettings } from '@/hooks/use-company-settings';
import { formatCurrency } from '@/lib/utils';

export type CustomerOption = {
    id: number;
    code: string;
    name: string;
};

export type ProductOption = {
    id: number;
    code: string;
    name: string;
};

export type ServiceOption = {
    id: number;
    code: string;
    name: string;
    price: string | number;
};

export type WarehouseOption = {
    id: number;
    code: string;
    name: string;
};

export type InvoiceLineData = {
    id?: number;
    product_id: number | null;
    service_id: number | null;
    description: string;
    quantity: string | number;
    unit_price: string | number;
    discount_rate: string | number;
    tax_rate: string | number;
};

export type InvoiceFormData = {
    id: number;
    code: string | null;
    display_code: string;
    customer_id: number | null;
    warehouse_id: number | null;
    issue_date: string | null;
    due_date: string | null;
    discount_amount: string | number;
    notes: string | null;
    lines: InvoiceLineData[];
};

type LineForm = {
    uid: string;
    product_id: string;
    service_id: string;
    description: string;
    quantity: string;
    unit_price: string;
    discount_rate: string;
    tax_rate: string;
};

type Props = {
    customers: CustomerOption[];
    products: ProductOption[];
    services: ServiceOption[];
    warehouses: WarehouseOption[];
    defaultTaxRate?: number;
    invoice?: InvoiceFormData | null;
    onCancel: () => void;
};

let uidCounter = 0;

function nextUid(): string {
    uidCounter += 1;

    return `inv-line-${uidCounter}`;
}

function round(value: number, decimals = 2): number {
    const factor = 10 ** decimals;

    return Math.round(value * factor) / factor;
}

function computeLine(
    quantity: number,
    unitPrice: number,
    discountRate: number,
    taxRate: number,
) {
    const gross = round(quantity * unitPrice);
    const lineSubtotal = round(gross * (1 - discountRate / 100));
    const lineTax = round(lineSubtotal * (taxRate / 100));
    const lineTotal = round(lineSubtotal + lineTax);

    return { lineSubtotal, lineTax, lineTotal };
}

function computeInvoiceTotals(
    lines: LineForm[],
    discountAmount: number,
) {
    let rawSubtotal = 0;
    let rawTax = 0;

    for (const line of lines) {
        const computed = computeLine(
            Number(line.quantity) || 0,
            Number(line.unit_price) || 0,
            Number(line.discount_rate) || 0,
            Number(line.tax_rate) || 0,
        );
        rawSubtotal += computed.lineSubtotal;
        rawTax += computed.lineTax;
    }

    rawSubtotal = round(rawSubtotal);
    rawTax = round(rawTax);

    const adjustedSubtotal = Math.max(0, round(rawSubtotal - discountAmount));
    const ratio = rawSubtotal > 0 ? adjustedSubtotal / rawSubtotal : 0;
    const adjustedTax = round(rawTax * ratio);
    const totalTtc = round(adjustedSubtotal + adjustedTax);

    return {
        subtotalHt: adjustedSubtotal,
        taxAmount: adjustedTax,
        totalTtc,
    };
}

function emptyLine(defaultTaxRate: number): LineForm {
    return {
        uid: nextUid(),
        product_id: '',
        service_id: '',
        description: '',
        quantity: '1',
        unit_price: '0',
        discount_rate: '0',
        tax_rate: String(defaultTaxRate),
    };
}

export function InvoiceForm({
    customers,
    products,
    services,
    warehouses,
    defaultTaxRate: defaultTaxRateProp,
    invoice = null,
    onCancel,
}: Props) {
    const isEditing = invoice !== null;
    const { symbol: currencySymbol } = useCompanyCurrency();
    const { defaultTaxRate: configuredTaxRate } = useCompanySettings();
    const defaultTaxRate = defaultTaxRateProp ?? configuredTaxRate;

    const form = useForm({
        customer_id: invoice?.customer_id ? String(invoice.customer_id) : '',
        warehouse_id: invoice?.warehouse_id ? String(invoice.warehouse_id) : '',
        issue_date: invoice?.issue_date ?? new Date().toISOString().slice(0, 10),
        due_date: invoice?.due_date ?? '',
        discount_amount: invoice?.discount_amount
            ? String(invoice.discount_amount)
            : '0',
        notes: invoice?.notes ?? '',
        lines:
            invoice && invoice.lines.length > 0
                ? invoice.lines.map(
                      (line): LineForm => ({
                          uid: nextUid(),
                          product_id: line.product_id
                              ? String(line.product_id)
                              : '',
                          service_id: line.service_id
                              ? String(line.service_id)
                              : '',
                          description: line.description,
                          quantity: String(line.quantity),
                          unit_price: String(line.unit_price),
                          discount_rate: String(line.discount_rate ?? 0),
                          tax_rate: String(line.tax_rate ?? defaultTaxRate),
                      }),
                  )
                : [emptyLine(defaultTaxRate)],
    });

    const lines = form.data.lines as LineForm[];
    const lineErrors = form.errors as Record<string, string>;

    function setLines(next: LineForm[]) {
        form.setData('lines', next);
    }

    function updateLine(index: number, patch: Partial<LineForm>) {
        setLines(
            lines.map((line, i) => (i === index ? { ...line, ...patch } : line)),
        );
    }

    function addLine() {
        setLines([...lines, emptyLine(defaultTaxRate)]);
    }

    function removeLine(index: number) {
        if (lines.length === 1) {
            return;
        }

        setLines(lines.filter((_, i) => i !== index));
    }

    function handleProductChange(index: number, productId: string) {
        const product = products.find((item) => String(item.id) === productId);
        const patch: Partial<LineForm> = {
            product_id: productId,
            service_id: '',
        };

        if (product && !lines[index]?.description.trim()) {
            patch.description = product.name;
        }

        updateLine(index, patch);
    }

    function handleServiceChange(index: number, serviceId: string) {
        const service = services.find((item) => String(item.id) === serviceId);
        const patch: Partial<LineForm> = {
            service_id: serviceId,
            product_id: '',
        };

        if (service) {
            if (!lines[index]?.description.trim()) {
                patch.description = service.name;
            }
            if (
                !lines[index]?.unit_price ||
                Number(lines[index]?.unit_price) === 0
            ) {
                patch.unit_price = String(service.price);
            }
        }

        updateLine(index, patch);
    }

    const discountAmount = Number(form.data.discount_amount) || 0;
    const totals = computeInvoiceTotals(lines, discountAmount);

    const selectedCustomer = customers.find(
        (customer) => String(customer.id) === form.data.customer_id,
    );

    const hasProductLines = lines.some((line) => line.product_id !== '');

    function submit(event: FormEvent) {
        event.preventDefault();

        const payload = {
            ...form.data,
            warehouse_id: form.data.warehouse_id
                ? Number(form.data.warehouse_id)
                : null,
            discount_amount: form.data.discount_amount || '0',
            lines: lines.map((line) => ({
                product_id: line.product_id ? Number(line.product_id) : null,
                service_id: line.service_id ? Number(line.service_id) : null,
                description: line.description,
                quantity: line.quantity,
                unit_price: line.unit_price,
                discount_rate: line.discount_rate || '0',
                tax_rate: line.tax_rate || String(defaultTaxRate),
            })),
        };

        form.transform(() => payload);

        if (isEditing && invoice) {
            form.put(InvoiceController.update.url(invoice.id));

            return;
        }

        form.post(InvoiceController.store.url());
    }

    return (
        <form onSubmit={submit}>
            <div className="grid gap-6 lg:grid-cols-3">
                <div className="space-y-6 lg:col-span-2">
                    <Card>
                        <CardHeader>
                            <CardTitle>Informations générales</CardTitle>
                            <CardDescription>
                                Choisissez le client et les dates de la facture.
                            </CardDescription>
                        </CardHeader>
                        <CardContent className="space-y-4">
                            {isEditing && invoice && (
                                <div className="grid max-w-xs gap-2">
                                    <Label>Référence</Label>
                                    <Input
                                        value={invoice.display_code}
                                        disabled
                                        readOnly
                                        className="font-mono"
                                    />
                                </div>
                            )}

                            <div className="grid gap-2">
                                <Label htmlFor="invoice-customer">
                                    Client
                                    <span className="ml-0.5 text-destructive">
                                        *
                                    </span>
                                </Label>
                                <Select
                                    value={form.data.customer_id || undefined}
                                    onValueChange={(value) =>
                                        form.setData('customer_id', value)
                                    }
                                >
                                    <SelectTrigger
                                        id="invoice-customer"
                                        className="w-full"
                                    >
                                        <SelectValue placeholder="Choisir un client…" />
                                    </SelectTrigger>
                                    <SelectContent>
                                        {customers.map((customer) => (
                                            <SelectItem
                                                key={customer.id}
                                                value={String(customer.id)}
                                            >
                                                {customer.name}
                                                <span className="ml-2 text-xs text-muted-foreground">
                                                    {customer.code}
                                                </span>
                                            </SelectItem>
                                        ))}
                                    </SelectContent>
                                </Select>
                                <InputError message={form.errors.customer_id} />
                            </div>

                            <div className="grid gap-2">
                                <Label htmlFor="invoice-warehouse">
                                    Entrepôt
                                    {hasProductLines && (
                                        <span className="ml-0.5 text-destructive">
                                            *
                                        </span>
                                    )}
                                </Label>
                                <Select
                                    value={form.data.warehouse_id || undefined}
                                    onValueChange={(value) =>
                                        form.setData('warehouse_id', value)
                                    }
                                >
                                    <SelectTrigger
                                        id="invoice-warehouse"
                                        className="w-full"
                                    >
                                        <SelectValue placeholder="Choisir un entrepôt…" />
                                    </SelectTrigger>
                                    <SelectContent>
                                        {warehouses.map((warehouse) => (
                                            <SelectItem
                                                key={warehouse.id}
                                                value={String(warehouse.id)}
                                            >
                                                {warehouse.name}
                                                <span className="ml-2 text-xs text-muted-foreground">
                                                    {warehouse.code}
                                                </span>
                                            </SelectItem>
                                        ))}
                                    </SelectContent>
                                </Select>
                                <p className="text-muted-foreground text-xs">
                                    {hasProductLines
                                        ? 'Obligatoire pour déduire le stock à l’émission.'
                                        : 'Optionnel si la facture ne contient que des prestations.'}
                                </p>
                                <InputError message={form.errors.warehouse_id} />
                            </div>

                            <div className="grid gap-4 sm:grid-cols-2">
                                <div className="grid gap-2">
                                    <Label htmlFor="invoice-issue-date">
                                        Date d'émission
                                        <span className="ml-0.5 text-destructive">
                                            *
                                        </span>
                                    </Label>
                                    <Input
                                        id="invoice-issue-date"
                                        type="date"
                                        value={form.data.issue_date}
                                        onChange={(event) =>
                                            form.setData(
                                                'issue_date',
                                                event.target.value,
                                            )
                                        }
                                        required
                                    />
                                    <InputError
                                        message={form.errors.issue_date}
                                    />
                                </div>

                                <div className="grid gap-2">
                                    <Label htmlFor="invoice-due-date">
                                        Date d'échéance
                                        <span className="ml-0.5 text-destructive">
                                            *
                                        </span>
                                    </Label>
                                    <Input
                                        id="invoice-due-date"
                                        type="date"
                                        value={form.data.due_date}
                                        onChange={(event) =>
                                            form.setData(
                                                'due_date',
                                                event.target.value,
                                            )
                                        }
                                        required
                                    />
                                    <InputError message={form.errors.due_date} />
                                </div>
                            </div>

                            <div className="grid max-w-xs gap-2">
                                <Label htmlFor="invoice-discount">
                                    Remise globale ({currencySymbol})
                                </Label>
                                <Input
                                    id="invoice-discount"
                                    type="number"
                                    step="1"
                                    min="0"
                                    inputMode="numeric"
                                    value={form.data.discount_amount}
                                    onChange={(event) =>
                                        form.setData(
                                            'discount_amount',
                                            event.target.value,
                                        )
                                    }
                                />
                                <InputError
                                    message={form.errors.discount_amount}
                                />
                            </div>

                            <div className="grid gap-2">
                                <Label htmlFor="invoice-notes">Notes</Label>
                                <Textarea
                                    id="invoice-notes"
                                    rows={3}
                                    placeholder="Informations complémentaires (optionnel)"
                                    value={form.data.notes ?? ''}
                                    onChange={(event) =>
                                        form.setData('notes', event.target.value)
                                    }
                                />
                                <InputError message={form.errors.notes} />
                            </div>
                        </CardContent>
                    </Card>

                    <Card>
                        <CardHeader className="flex-row items-center justify-between space-y-0">
                            <div className="space-y-1.5">
                                <CardTitle>Lignes de facture</CardTitle>
                                <CardDescription>
                                    {lines.length}{' '}
                                    {lines.length > 1 ? 'lignes' : 'ligne'} ·
                                    associez un produit, une prestation ou
                                    saisissez une ligne libre.
                                </CardDescription>
                            </div>
                            <Button
                                type="button"
                                variant="outline"
                                size="sm"
                                onClick={addLine}
                            >
                                <Plus />
                                Ajouter
                            </Button>
                        </CardHeader>
                        <CardContent className="space-y-4">
                            {lines.map((line, index) => {
                                const lineTotals = computeLine(
                                    Number(line.quantity) || 0,
                                    Number(line.unit_price) || 0,
                                    Number(line.discount_rate) || 0,
                                    Number(line.tax_rate) || 0,
                                );

                                return (
                                    <div
                                        key={line.uid}
                                        className="overflow-hidden rounded-xl border bg-background"
                                    >
                                        <div className="flex items-center justify-between gap-2 border-b bg-muted/40 px-4 py-2.5">
                                            <div className="flex items-center gap-2.5">
                                                <span className="flex size-6 items-center justify-center rounded-full bg-primary/10 text-xs font-semibold text-primary">
                                                    {index + 1}
                                                </span>
                                                <span className="text-sm font-medium">
                                                    {lineTotals.lineTotal > 0
                                                        ? formatCurrency(
                                                              lineTotals.lineTotal,
                                                          )
                                                        : 'Nouvelle ligne'}
                                                </span>
                                            </div>
                                            <Button
                                                type="button"
                                                variant="ghost"
                                                size="icon"
                                                className="size-8 text-muted-foreground hover:text-destructive"
                                                onClick={() => removeLine(index)}
                                                disabled={lines.length === 1}
                                                aria-label="Supprimer la ligne"
                                            >
                                                <Trash2 />
                                            </Button>
                                        </div>

                                        <div className="grid gap-4 p-4 sm:grid-cols-2">
                                            <div className="grid gap-2">
                                                <Label className="text-xs text-muted-foreground">
                                                    Produit (optionnel)
                                                </Label>
                                                <Select
                                                    value={
                                                        line.product_id ||
                                                        undefined
                                                    }
                                                    onValueChange={(value) =>
                                                        handleProductChange(
                                                            index,
                                                            value,
                                                        )
                                                    }
                                                >
                                                    <SelectTrigger className="w-full">
                                                        <SelectValue placeholder="Choisir un produit…" />
                                                    </SelectTrigger>
                                                    <SelectContent>
                                                        {products.map(
                                                            (product) => (
                                                                <SelectItem
                                                                    key={
                                                                        product.id
                                                                    }
                                                                    value={String(
                                                                        product.id,
                                                                    )}
                                                                >
                                                                    {
                                                                        product.name
                                                                    }
                                                                    <span className="ml-2 text-xs text-muted-foreground">
                                                                        {
                                                                            product.code
                                                                        }
                                                                    </span>
                                                                </SelectItem>
                                                            ),
                                                        )}
                                                    </SelectContent>
                                                </Select>
                                                <InputError
                                                    message={
                                                        lineErrors[
                                                            `lines.${index}.product_id`
                                                        ]
                                                    }
                                                />
                                            </div>

                                            <div className="grid gap-2">
                                                <Label className="text-xs text-muted-foreground">
                                                    Prestation (optionnel)
                                                </Label>
                                                <Select
                                                    value={
                                                        line.service_id ||
                                                        undefined
                                                    }
                                                    onValueChange={(value) =>
                                                        handleServiceChange(
                                                            index,
                                                            value,
                                                        )
                                                    }
                                                >
                                                    <SelectTrigger className="w-full">
                                                        <SelectValue placeholder="Choisir une prestation…" />
                                                    </SelectTrigger>
                                                    <SelectContent>
                                                        {services.map(
                                                            (service) => (
                                                                <SelectItem
                                                                    key={
                                                                        service.id
                                                                    }
                                                                    value={String(
                                                                        service.id,
                                                                    )}
                                                                >
                                                                    {
                                                                        service.name
                                                                    }
                                                                    <span className="ml-2 text-xs text-muted-foreground">
                                                                        {
                                                                            service.code
                                                                        }
                                                                    </span>
                                                                </SelectItem>
                                                            ),
                                                        )}
                                                    </SelectContent>
                                                </Select>
                                                <InputError
                                                    message={
                                                        lineErrors[
                                                            `lines.${index}.service_id`
                                                        ]
                                                    }
                                                />
                                            </div>

                                            <div className="grid gap-2 sm:col-span-2">
                                                <Label className="text-xs text-muted-foreground">
                                                    Description
                                                    <span className="ml-0.5 text-destructive">
                                                        *
                                                    </span>
                                                </Label>
                                                <Input
                                                    value={line.description}
                                                    onChange={(event) =>
                                                        updateLine(index, {
                                                            description:
                                                                event.target
                                                                    .value,
                                                        })
                                                    }
                                                    required
                                                />
                                                <InputError
                                                    message={
                                                        lineErrors[
                                                            `lines.${index}.description`
                                                        ]
                                                    }
                                                />
                                            </div>

                                            <div className="grid gap-2">
                                                <Label className="text-xs text-muted-foreground">
                                                    Quantité
                                                </Label>
                                                <Input
                                                    type="number"
                                                    step="0.01"
                                                    min="0.01"
                                                    inputMode="decimal"
                                                    value={line.quantity}
                                                    onChange={(event) =>
                                                        updateLine(index, {
                                                            quantity:
                                                                event.target
                                                                    .value,
                                                        })
                                                    }
                                                    required
                                                />
                                                <InputError
                                                    message={
                                                        lineErrors[
                                                            `lines.${index}.quantity`
                                                        ]
                                                    }
                                                />
                                            </div>

                                            <div className="grid gap-2">
                                                <Label className="text-xs text-muted-foreground">
                                                    Prix unitaire ({currencySymbol})
                                                </Label>
                                                <Input
                                                    type="number"
                                                    step="1"
                                                    min="0"
                                                    inputMode="numeric"
                                                    placeholder="0"
                                                    value={line.unit_price}
                                                    onChange={(event) =>
                                                        updateLine(index, {
                                                            unit_price:
                                                                event.target
                                                                    .value,
                                                        })
                                                    }
                                                    required
                                                />
                                                <InputError
                                                    message={
                                                        lineErrors[
                                                            `lines.${index}.unit_price`
                                                        ]
                                                    }
                                                />
                                            </div>

                                            <div className="grid gap-2">
                                                <Label className="text-xs text-muted-foreground">
                                                    Remise (%)
                                                </Label>
                                                <Input
                                                    type="number"
                                                    step="0.01"
                                                    min="0"
                                                    max="100"
                                                    inputMode="decimal"
                                                    value={line.discount_rate}
                                                    onChange={(event) =>
                                                        updateLine(index, {
                                                            discount_rate:
                                                                event.target
                                                                    .value,
                                                        })
                                                    }
                                                />
                                                <InputError
                                                    message={
                                                        lineErrors[
                                                            `lines.${index}.discount_rate`
                                                        ]
                                                    }
                                                />
                                            </div>

                                            <div className="grid gap-2">
                                                <Label className="text-xs text-muted-foreground">
                                                    TVA (%)
                                                </Label>
                                                <Input
                                                    type="number"
                                                    step="0.01"
                                                    min="0"
                                                    max="100"
                                                    inputMode="decimal"
                                                    value={line.tax_rate}
                                                    onChange={(event) =>
                                                        updateLine(index, {
                                                            tax_rate:
                                                                event.target
                                                                    .value,
                                                        })
                                                    }
                                                />
                                                <InputError
                                                    message={
                                                        lineErrors[
                                                            `lines.${index}.tax_rate`
                                                        ]
                                                    }
                                                />
                                            </div>
                                        </div>
                                    </div>
                                );
                            })}

                            <InputError message={form.errors.lines} />

                            <Button
                                type="button"
                                variant="ghost"
                                className="w-full border border-dashed text-muted-foreground hover:text-foreground"
                                onClick={addLine}
                            >
                                <Plus />
                                Ajouter une ligne
                            </Button>
                        </CardContent>
                    </Card>
                </div>

                <div className="lg:col-span-1">
                    <Card className="lg:sticky lg:top-6">
                        <CardHeader>
                            <CardTitle>Récapitulatif</CardTitle>
                            <CardDescription>
                                Vérifiez la facture avant d'enregistrer.
                            </CardDescription>
                        </CardHeader>
                        <CardContent className="space-y-4">
                            <div className="flex items-center justify-between gap-4 text-sm">
                                <span className="text-muted-foreground">
                                    Client
                                </span>
                                <span className="truncate text-right font-medium">
                                    {selectedCustomer?.name ?? '—'}
                                </span>
                            </div>
                            <div className="flex items-center justify-between text-sm">
                                <span className="text-muted-foreground">
                                    Nombre de lignes
                                </span>
                                <span className="flex items-center gap-1.5 font-medium tabular-nums">
                                    <Package className="size-4 text-muted-foreground" />
                                    {lines.length}
                                </span>
                            </div>
                            <div className="space-y-2 border-t pt-4 text-sm">
                                <div className="flex items-center justify-between">
                                    <span className="text-muted-foreground">
                                        Total HT
                                    </span>
                                    <span className="font-medium tabular-nums">
                                        {formatCurrency(totals.subtotalHt)}
                                    </span>
                                </div>
                                <div className="flex items-center justify-between">
                                    <span className="text-muted-foreground">
                                        TVA
                                    </span>
                                    <span className="font-medium tabular-nums">
                                        {formatCurrency(totals.taxAmount)}
                                    </span>
                                </div>
                                <div className="flex items-center justify-between border-t pt-2">
                                    <span className="font-medium">
                                        Total TTC
                                    </span>
                                    <span className="text-xl font-semibold tabular-nums">
                                        {formatCurrency(totals.totalTtc)}
                                    </span>
                                </div>
                            </div>

                            <div className="flex flex-col gap-2 pt-2">
                                <Button
                                    type="submit"
                                    disabled={form.processing}
                                >
                                    {isEditing
                                        ? 'Enregistrer les modifications'
                                        : 'Créer la facture'}
                                </Button>
                                <Button
                                    type="button"
                                    variant="outline"
                                    onClick={onCancel}
                                >
                                    Annuler
                                </Button>
                            </div>
                        </CardContent>
                    </Card>
                </div>
            </div>
        </form>
    );
}
