import { useForm } from '@inertiajs/react';
import { Plus, Trash2 } from 'lucide-react';
import type { FormEvent } from 'react';
import JournalEntryController from '@/actions/App/Http/Controllers/Admin/JournalEntryController';
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 { cn, formatCurrency } from '@/lib/utils';

export type AccountOption = {
    id: number;
    code: string;
    label: string;
};

export type CostCenterOption = {
    id: number;
    code: string;
    name: string;
    department_id: number | null;
};

export type JournalTypeOption = { value: string; label: string };

export type JournalEntryLineData = {
    id?: number;
    chart_of_account_id: number;
    cost_center_id?: number | null;
    label: string | null;
    debit: string | number;
    credit: string | number;
};

export type JournalEntryFormData = {
    id: number;
    journal_type: string;
    entry_date: string | null;
    label: string;
    lines: JournalEntryLineData[];
};

type LineForm = {
    uid: string;
    chart_of_account_id: string;
    cost_center_id: string;
    label: string;
    debit: string;
    credit: string;
};

type Props = {
    accounts: AccountOption[];
    costCenters?: CostCenterOption[];
    journalTypes: JournalTypeOption[];
    entry?: JournalEntryFormData | null;
    onCancel: () => void;
};

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

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

function emptyLine(): LineForm {
    return {
        uid: nextUid(),
        chart_of_account_id: '',
        cost_center_id: '',
        label: '',
        debit: '0',
        credit: '0',
    };
}

export function JournalEntryForm({
    accounts,
    costCenters = [],
    journalTypes,
    entry = null,
    onCancel,
}: Props) {
    const isEditing = entry !== null;

    const form = useForm({
        journal_type: entry?.journal_type ?? journalTypes[0]?.value ?? '',
        entry_date: entry?.entry_date ?? new Date().toISOString().slice(0, 10),
        label: entry?.label ?? '',
        lines:
            entry && entry.lines.length > 0
                ? entry.lines.map(
                      (line): LineForm => ({
                          uid: nextUid(),
                          chart_of_account_id: String(line.chart_of_account_id),
                          cost_center_id: line.cost_center_id
                              ? String(line.cost_center_id)
                              : '',
                          label: line.label ?? '',
                          debit: String(line.debit),
                          credit: String(line.credit),
                      }),
                  )
                : [emptyLine(), emptyLine()],
    });

    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()]);
    }

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

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

    const totalDebit = lines.reduce(
        (sum, line) => sum + (Number(line.debit) || 0),
        0,
    );
    const totalCredit = lines.reduce(
        (sum, line) => sum + (Number(line.credit) || 0),
        0,
    );
    const isBalanced =
        Math.abs(totalDebit - totalCredit) < 0.01 && totalDebit > 0;

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

        const payload = {
            ...form.data,
            lines: lines.map((line) => ({
                chart_of_account_id: line.chart_of_account_id
                    ? Number(line.chart_of_account_id)
                    : null,
                cost_center_id: line.cost_center_id
                    ? Number(line.cost_center_id)
                    : null,
                label: line.label || null,
                debit: line.debit || 0,
                credit: line.credit || 0,
            })),
        };

        form.transform(() => payload);

        if (isEditing && entry) {
            form.put(JournalEntryController.update.url(entry.id));

            return;
        }

        form.post(JournalEntryController.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>
                                Journal, date et libellé de l'écriture.
                            </CardDescription>
                        </CardHeader>
                        <CardContent className="space-y-4">
                            <div className="grid gap-4 sm:grid-cols-2">
                                <div className="grid gap-2">
                                    <Label htmlFor="je-journal-type">
                                        Journal
                                        <span className="ml-0.5 text-destructive">
                                            *
                                        </span>
                                    </Label>
                                    <Select
                                        value={form.data.journal_type || undefined}
                                        onValueChange={(value) =>
                                            form.setData('journal_type', value)
                                        }
                                    >
                                        <SelectTrigger
                                            id="je-journal-type"
                                            className="w-full"
                                        >
                                            <SelectValue placeholder="Choisir un journal…" />
                                        </SelectTrigger>
                                        <SelectContent>
                                            {journalTypes.map((type) => (
                                                <SelectItem
                                                    key={type.value}
                                                    value={type.value}
                                                >
                                                    {type.label}
                                                </SelectItem>
                                            ))}
                                        </SelectContent>
                                    </Select>
                                    <InputError
                                        message={form.errors.journal_type}
                                    />
                                </div>

                                <div className="grid gap-2">
                                    <Label htmlFor="je-entry-date">
                                        Date
                                        <span className="ml-0.5 text-destructive">
                                            *
                                        </span>
                                    </Label>
                                    <Input
                                        id="je-entry-date"
                                        type="date"
                                        value={form.data.entry_date ?? ''}
                                        onChange={(event) =>
                                            form.setData(
                                                'entry_date',
                                                event.target.value,
                                            )
                                        }
                                        required
                                    />
                                    <InputError
                                        message={form.errors.entry_date}
                                    />
                                </div>
                            </div>

                            <div className="grid gap-2">
                                <Label htmlFor="je-label">
                                    Libellé
                                    <span className="ml-0.5 text-destructive">
                                        *
                                    </span>
                                </Label>
                                <Input
                                    id="je-label"
                                    value={form.data.label}
                                    onChange={(event) =>
                                        form.setData('label', event.target.value)
                                    }
                                    placeholder="Objet de l'écriture"
                                    required
                                />
                                <InputError message={form.errors.label} />
                            </div>
                        </CardContent>
                    </Card>

                    <Card>
                        <CardHeader className="flex-row items-center justify-between space-y-0">
                            <div className="space-y-1.5">
                                <CardTitle>Lignes d'écriture</CardTitle>
                                <CardDescription>
                                    {lines.length}{' '}
                                    {lines.length > 1 ? 'lignes' : 'ligne'} ·
                                    chaque ligne doit renseigner un débit ou un
                                    crédit.
                                </CardDescription>
                            </div>
                            <Button
                                type="button"
                                variant="outline"
                                size="sm"
                                onClick={addLine}
                            >
                                <Plus />
                                Ajouter
                            </Button>
                        </CardHeader>
                        <CardContent className="space-y-4">
                            {lines.map((line, index) => (
                                <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">
                                        <span className="flex size-6 items-center justify-center rounded-full bg-primary/10 text-xs font-semibold text-primary">
                                            {index + 1}
                                        </span>
                                        <Button
                                            type="button"
                                            variant="ghost"
                                            size="icon"
                                            className="size-8 text-muted-foreground hover:text-destructive"
                                            onClick={() => removeLine(index)}
                                            disabled={lines.length <= 2}
                                            aria-label="Supprimer la ligne"
                                        >
                                            <Trash2 />
                                        </Button>
                                    </div>

                                    <div className="grid gap-4 p-4 sm:grid-cols-2 lg:grid-cols-5">
                                        <div className="grid gap-2 sm:col-span-2">
                                            <Label className="text-xs text-muted-foreground">
                                                Compte
                                                <span className="ml-0.5 text-destructive">
                                                    *
                                                </span>
                                            </Label>
                                            <Select
                                                value={
                                                    line.chart_of_account_id ||
                                                    undefined
                                                }
                                                onValueChange={(value) =>
                                                    updateLine(index, {
                                                        chart_of_account_id:
                                                            value,
                                                    })
                                                }
                                            >
                                                <SelectTrigger className="w-full">
                                                    <SelectValue placeholder="Choisir un compte…" />
                                                </SelectTrigger>
                                                <SelectContent>
                                                    {accounts.map((account) => (
                                                        <SelectItem
                                                            key={account.id}
                                                            value={String(
                                                                account.id,
                                                            )}
                                                        >
                                                            {account.code} —{' '}
                                                            {account.label}
                                                        </SelectItem>
                                                    ))}
                                                </SelectContent>
                                            </Select>
                                            <InputError
                                                message={
                                                    lineErrors[
                                                        `lines.${index}.chart_of_account_id`
                                                    ]
                                                }
                                            />
                                        </div>

                                        {costCenters.length > 0 && (
                                            <div className="grid gap-2 sm:col-span-2 lg:col-span-1">
                                                <Label className="text-xs text-muted-foreground">
                                                    Centre de coût
                                                </Label>
                                                <Select
                                                    value={
                                                        line.cost_center_id ||
                                                        '__none__'
                                                    }
                                                    onValueChange={(value) =>
                                                        updateLine(index, {
                                                            cost_center_id:
                                                                value ===
                                                                '__none__'
                                                                    ? ''
                                                                    : value,
                                                        })
                                                    }
                                                >
                                                    <SelectTrigger className="w-full">
                                                        <SelectValue placeholder="Aucun" />
                                                    </SelectTrigger>
                                                    <SelectContent>
                                                        <SelectItem value="__none__">
                                                            Aucun
                                                        </SelectItem>
                                                        {costCenters.map(
                                                            (center) => (
                                                                <SelectItem
                                                                    key={
                                                                        center.id
                                                                    }
                                                                    value={String(
                                                                        center.id,
                                                                    )}
                                                                >
                                                                    {
                                                                        center.code
                                                                    }{' '}
                                                                    —{' '}
                                                                    {
                                                                        center.name
                                                                    }
                                                                </SelectItem>
                                                            ),
                                                        )}
                                                    </SelectContent>
                                                </Select>
                                            </div>
                                        )}

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

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

                                        <div className="grid gap-2 sm:col-span-4">
                                            <Label className="text-xs text-muted-foreground">
                                                Libellé de ligne (optionnel)
                                            </Label>
                                            <Input
                                                value={line.label}
                                                onChange={(event) =>
                                                    updateLine(index, {
                                                        label: event.target
                                                            .value,
                                                    })
                                                }
                                            />
                                        </div>
                                    </div>
                                </div>
                            ))}

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

                <div className="lg:col-span-1">
                    <Card className="lg:sticky lg:top-6">
                        <CardHeader>
                            <CardTitle>Équilibre</CardTitle>
                            <CardDescription>
                                Le total débit doit égaler le total crédit.
                            </CardDescription>
                        </CardHeader>
                        <CardContent className="space-y-4">
                            <div className="flex items-center justify-between text-sm">
                                <span className="text-muted-foreground">
                                    Total débit
                                </span>
                                <span className="font-medium tabular-nums">
                                    {formatCurrency(totalDebit)}
                                </span>
                            </div>
                            <div className="flex items-center justify-between text-sm">
                                <span className="text-muted-foreground">
                                    Total crédit
                                </span>
                                <span className="font-medium tabular-nums">
                                    {formatCurrency(totalCredit)}
                                </span>
                            </div>
                            <div
                                className={cn(
                                    'flex items-center justify-between border-t pt-4 text-sm font-medium',
                                    isBalanced
                                        ? 'text-emerald-600 dark:text-emerald-400'
                                        : 'text-destructive',
                                )}
                            >
                                <span>
                                    {isBalanced ? 'Équilibrée' : 'Déséquilibrée'}
                                </span>
                                <span className="tabular-nums">
                                    {formatCurrency(
                                        Math.abs(totalDebit - totalCredit),
                                    )}
                                </span>
                            </div>

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