import { Head, router, useForm } from '@inertiajs/react';
import {
    AlertTriangle,
    ArrowDownLeft,
    ArrowUpRight,
    Banknote,
    Plus,
    Receipt,
    Save,
    Wallet,
} from 'lucide-react';
import { useEffect, useState } from 'react';
import { Field } from '@/components/crm/form-field';
import { KpiTile } from '@/components/crm/kpi-tile';
import { NativeSelect } from '@/components/crm/native-select';
import { PageHeader } from '@/components/crm/page-header';
import { StatusLabel } from '@/components/crm/status-badge';
import {
    DataPagination,
    FilterBar,
    FilterField,
    SearchInput,
    SegmentedControl,
    TableCard,
} from '@/components/crm/toolbar';
import { Button } from '@/components/ui/button';
import {
    Dialog,
    DialogContent,
    DialogDescription,
    DialogFooter,
    DialogHeader,
    DialogTitle,
} from '@/components/ui/dialog';
import { Input } from '@/components/ui/input';
import {
    Table,
    TableBody,
    TableCell,
    TableHead,
    TableHeader,
    TableRow,
} from '@/components/ui/table';
import {
    formatCompactCurrency,
    formatCurrency,
    formatDate,
} from '@/lib/format';
import {
    index as invoicesIndex,
    payment,
    store,
} from '@/routes/finance/invoices';
import type { Paginated } from '@/types';

type Option = { value: string; label: string };
type StatusOption = Option & { color: string };

type InvoiceRow = {
    id: number;
    invoice_number: string;
    direction: Option;
    status: StatusOption;
    party: string;
    customer: { id: number; name: string } | null;
    supplier: { id: number; name: string } | null;
    job: { id: number; job_number: string } | null;
    invoice_date: string | null;
    due_date: string | null;
    amount: string;
    gst_amount: string | null;
    amount_paid: string;
    balance: number;
    is_overdue: boolean;
    is_partially_paid: boolean;
    paid_on: string | null;
    reference: string | null;
};

type Filters = {
    search: string | null;
    status: string | null;
    direction: string;
    scope: string;
};

export default function Invoices({
    invoices,
    filters,
    options,
    stats,
    can,
}: {
    invoices: Paginated<InvoiceRow>;
    filters: Filters;
    options: {
        invoiceStatuses: StatusOption[];
        directions: Option[];
        customers: Array<{ value: number; label: string }>;
        jobs: Array<{ value: number; label: string }>;
    };
    stats: {
        receivable: number;
        receivable_paid: number;
        overdue: number;
        payable: number;
        paid_this_month: number;
        overdue_count: number;
    };
    can: { manage: boolean };
}) {
    const [search, setSearch] = useState(filters.search ?? '');
    const [showCreate, setShowCreate] = useState(false);
    const [paying, setPaying] = useState<InvoiceRow | null>(null);

    useEffect(() => {
        setSearch(filters.search ?? '');
    }, [filters.search]);

    const apply = (changes: Partial<Filters>) => {
        router.get(
            invoicesIndex().url,
            { ...filters, ...changes, search: search || null },
            { preserveState: true, preserveScroll: true, replace: true },
        );
    };

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

            <div className="flex flex-col gap-5">
                <PageHeader
                    eyebrow={
                        <>
                            <Receipt className="size-3.5 text-solar" />
                            <span>Finance</span>
                        </>
                    }
                    title="Invoices"
                    description="Money owed to the company and money it owes. Status is worked out from the payments recorded against each invoice, never typed by hand."
                    actions={
                        can.manage ? (
                            <Button
                                size="sm"
                                onClick={() => setShowCreate(true)}
                            >
                                <Plus className="size-4" />
                                New invoice
                            </Button>
                        ) : undefined
                    }
                />

                <div className="grid gap-3 sm:grid-cols-2 xl:grid-cols-4">
                    <KpiTile
                        label="Still to collect"
                        value={formatCompactCurrency(
                            stats.receivable - stats.receivable_paid,
                        )}
                        hint={`${formatCompactCurrency(stats.receivable)} invoiced`}
                        icon={ArrowDownLeft}
                        tone="blue"
                    />
                    <KpiTile
                        label="Overdue"
                        value={formatCompactCurrency(stats.overdue)}
                        hint={`${stats.overdue_count} invoices past due`}
                        icon={AlertTriangle}
                        tone={stats.overdue_count > 0 ? 'danger' : 'slate'}
                    />
                    <KpiTile
                        label="Owed to suppliers"
                        value={formatCompactCurrency(stats.payable)}
                        hint="Supplier invoices outstanding"
                        icon={ArrowUpRight}
                        tone="warning"
                    />
                    <KpiTile
                        label="Paid this month"
                        value={formatCompactCurrency(stats.paid_this_month)}
                        hint="Settled invoices"
                        icon={Banknote}
                        tone="success"
                    />
                </div>

                <form
                    onSubmit={(event) => {
                        event.preventDefault();
                        apply({});
                    }}
                >
                    <FilterBar
                        actions={
                            <SegmentedControl
                                value={filters.scope}
                                onChange={(scope) => apply({ scope })}
                                options={[
                                    {
                                        value: 'outstanding',
                                        label: 'Outstanding',
                                    },
                                    { value: 'overdue', label: 'Overdue' },
                                    { value: 'all', label: 'All' },
                                ]}
                            />
                        }
                    >
                        <SearchInput
                            value={search}
                            onChange={setSearch}
                            placeholder="Invoice number, party or reference"
                        />
                        <SegmentedControl
                            value={filters.direction}
                            onChange={(direction) => apply({ direction })}
                            options={[
                                { value: 'customer', label: 'Receivable' },
                                { value: 'supplier', label: 'Payable' },
                                { value: 'all', label: 'Both' },
                            ]}
                        />
                        <FilterField label="Status">
                            <NativeSelect
                                value={filters.status ?? ''}
                                onChange={(event) =>
                                    apply({
                                        status: event.target.value || null,
                                    })
                                }
                                className="h-9 w-44"
                            >
                                <option value="">Any status</option>
                                {options.invoiceStatuses.map((status) => (
                                    <option
                                        key={status.value}
                                        value={status.value}
                                    >
                                        {status.label}
                                    </option>
                                ))}
                            </NativeSelect>
                        </FilterField>
                    </FilterBar>
                </form>

                <TableCard>
                    <div className="crm-scroll overflow-x-auto">
                        <Table>
                            <TableHeader>
                                <TableRow className="hover:bg-transparent">
                                    <TableHead>Invoice</TableHead>
                                    <TableHead>Party</TableHead>
                                    <TableHead>Status</TableHead>
                                    <TableHead>Dates</TableHead>
                                    <TableHead className="text-right">
                                        Amount
                                    </TableHead>
                                    <TableHead className="text-right">
                                        Balance
                                    </TableHead>
                                    {can.manage ? <TableHead /> : null}
                                </TableRow>
                            </TableHeader>
                            <TableBody>
                                {invoices.data.length === 0 ? (
                                    <TableRow>
                                        <TableCell
                                            colSpan={can.manage ? 7 : 6}
                                            className="px-4 py-10 text-center text-sm text-muted-foreground"
                                        >
                                            No invoices match these filters.
                                        </TableCell>
                                    </TableRow>
                                ) : null}
                                {invoices.data.map((invoice) => (
                                    <TableRow key={invoice.id}>
                                        <TableCell>
                                            <span className="block font-mono text-xs">
                                                {invoice.invoice_number}
                                            </span>
                                            {invoice.job ? (
                                                <span className="block text-[11px] text-muted-foreground">
                                                    {invoice.job.job_number}
                                                </span>
                                            ) : null}
                                        </TableCell>
                                        <TableCell className="text-sm">
                                            {invoice.party}
                                            <span className="block text-[11px] text-muted-foreground">
                                                {invoice.direction.label}
                                            </span>
                                        </TableCell>
                                        <TableCell>
                                            <StatusLabel
                                                tone={invoice.status.color}
                                            >
                                                {invoice.status.label}
                                            </StatusLabel>
                                        </TableCell>
                                        <TableCell className="text-xs text-muted-foreground">
                                            <span className="block">
                                                Issued{' '}
                                                {formatDate(
                                                    invoice.invoice_date,
                                                )}
                                            </span>
                                            <span
                                                className={
                                                    invoice.is_overdue
                                                        ? 'block font-medium text-destructive'
                                                        : 'block'
                                                }
                                            >
                                                Due{' '}
                                                {formatDate(invoice.due_date)}
                                            </span>
                                        </TableCell>
                                        <TableCell className="text-right text-sm crm-num">
                                            {formatCurrency(
                                                Number(invoice.amount),
                                            )}
                                            <span className="block text-[11px] text-muted-foreground">
                                                Paid{' '}
                                                {formatCurrency(
                                                    Number(invoice.amount_paid),
                                                )}
                                            </span>
                                        </TableCell>
                                        <TableCell className="text-right text-sm font-medium crm-num">
                                            {formatCurrency(invoice.balance)}
                                        </TableCell>
                                        {can.manage ? (
                                            <TableCell className="text-right">
                                                {invoice.status.value !==
                                                    'paid' &&
                                                invoice.status.value !==
                                                    'cancelled' ? (
                                                    <Button
                                                        size="sm"
                                                        variant="ghost"
                                                        onClick={() =>
                                                            setPaying(invoice)
                                                        }
                                                    >
                                                        <Wallet className="size-4" />
                                                        Payment
                                                    </Button>
                                                ) : null}
                                            </TableCell>
                                        ) : null}
                                    </TableRow>
                                ))}
                            </TableBody>
                        </Table>
                    </div>
                    <DataPagination page={invoices} noun="invoices" />
                </TableCard>
            </div>

            {can.manage ? (
                <>
                    <InvoiceDialog
                        open={showCreate}
                        options={options}
                        onClose={() => setShowCreate(false)}
                    />
                    {paying ? (
                        <PaymentDialog
                            invoice={paying}
                            onClose={() => setPaying(null)}
                        />
                    ) : null}
                </>
            ) : null}
        </>
    );
}

function InvoiceDialog({
    open,
    options,
    onClose,
}: {
    open: boolean;
    options: {
        directions: Option[];
        customers: Array<{ value: number; label: string }>;
        jobs: Array<{ value: number; label: string }>;
    };
    onClose: () => void;
}) {
    const form = useForm({
        direction: 'customer',
        status: 'issued',
        customer_id: '',
        job_id: '',
        invoice_date: '',
        due_date: '',
        amount: '',
        gst_amount: '',
        reference: '',
        notes: '',
    });

    const submit = () => {
        form.post(store().url, {
            preserveScroll: true,
            onSuccess: () => {
                form.reset();
                onClose();
            },
        });
    };

    return (
        <Dialog open={open} onOpenChange={(state) => !state && onClose()}>
            <DialogContent className="sm:max-w-xl">
                <DialogHeader>
                    <DialogTitle>New invoice</DialogTitle>
                    <DialogDescription>
                        Enter the amount including GST. Record payments against
                        it afterwards and the status follows.
                    </DialogDescription>
                </DialogHeader>

                <form
                    onSubmit={(event) => {
                        event.preventDefault();
                        submit();
                    }}
                    className="grid gap-3 sm:grid-cols-2"
                >
                    <Field label="Direction" error={form.errors.direction}>
                        <NativeSelect
                            value={form.data.direction}
                            onChange={(event) =>
                                form.setData('direction', event.target.value)
                            }
                        >
                            {options.directions.map((direction) => (
                                <option
                                    key={direction.value}
                                    value={direction.value}
                                >
                                    {direction.label}
                                </option>
                            ))}
                        </NativeSelect>
                    </Field>
                    <Field label="Customer" error={form.errors.customer_id}>
                        <NativeSelect
                            value={form.data.customer_id}
                            onChange={(event) =>
                                form.setData('customer_id', event.target.value)
                            }
                        >
                            <option value="">Choose a customer</option>
                            {options.customers.map((customer) => (
                                <option
                                    key={customer.value}
                                    value={customer.value}
                                >
                                    {customer.label}
                                </option>
                            ))}
                        </NativeSelect>
                    </Field>
                    <Field label="Job" error={form.errors.job_id}>
                        <NativeSelect
                            value={form.data.job_id}
                            onChange={(event) =>
                                form.setData('job_id', event.target.value)
                            }
                        >
                            <option value="">Not tied to a job</option>
                            {options.jobs.map((job) => (
                                <option key={job.value} value={job.value}>
                                    {job.label}
                                </option>
                            ))}
                        </NativeSelect>
                    </Field>
                    <Field
                        label="Amount (inc GST)"
                        error={form.errors.amount}
                        required
                    >
                        <Input
                            type="number"
                            min={0}
                            step="0.01"
                            value={form.data.amount}
                            onChange={(event) =>
                                form.setData('amount', event.target.value)
                            }
                            required
                        />
                    </Field>
                    <Field
                        label="Invoice date"
                        error={form.errors.invoice_date}
                    >
                        <Input
                            type="date"
                            value={form.data.invoice_date}
                            onChange={(event) =>
                                form.setData('invoice_date', event.target.value)
                            }
                        />
                    </Field>
                    <Field label="Due date" error={form.errors.due_date}>
                        <Input
                            type="date"
                            value={form.data.due_date}
                            onChange={(event) =>
                                form.setData('due_date', event.target.value)
                            }
                        />
                    </Field>
                    <Field label="Reference" error={form.errors.reference}>
                        <Input
                            value={form.data.reference}
                            onChange={(event) =>
                                form.setData('reference', event.target.value)
                            }
                        />
                    </Field>
                    <Field label="Notes" error={form.errors.notes}>
                        <Input
                            value={form.data.notes}
                            onChange={(event) =>
                                form.setData('notes', event.target.value)
                            }
                        />
                    </Field>

                    <DialogFooter className="sm:col-span-2">
                        <Button type="button" variant="ghost" onClick={onClose}>
                            Cancel
                        </Button>
                        <Button type="submit" disabled={form.processing}>
                            <Save className="size-4" />
                            Create invoice
                        </Button>
                    </DialogFooter>
                </form>
            </DialogContent>
        </Dialog>
    );
}

function PaymentDialog({
    invoice,
    onClose,
}: {
    invoice: InvoiceRow;
    onClose: () => void;
}) {
    const form = useForm({
        amount: invoice.balance > 0 ? String(invoice.balance) : '',
        paid_on: new Date().toISOString().slice(0, 10),
        reference: '',
    });

    const submit = () => {
        form.post(payment(invoice.id).url, {
            preserveScroll: true,
            onSuccess: onClose,
        });
    };

    return (
        <Dialog open onOpenChange={(state) => !state && onClose()}>
            <DialogContent className="sm:max-w-md">
                <DialogHeader>
                    <DialogTitle>Record payment</DialogTitle>
                    <DialogDescription>
                        {invoice.invoice_number} ·{' '}
                        {formatCurrency(invoice.balance)} outstanding.
                    </DialogDescription>
                </DialogHeader>

                <form
                    onSubmit={(event) => {
                        event.preventDefault();
                        submit();
                    }}
                    className="grid gap-3"
                >
                    <Field label="Amount" error={form.errors.amount} required>
                        <Input
                            type="number"
                            min={0.01}
                            step="0.01"
                            value={form.data.amount}
                            onChange={(event) =>
                                form.setData('amount', event.target.value)
                            }
                            required
                        />
                    </Field>
                    <Field
                        label="Received on"
                        error={form.errors.paid_on}
                        required
                    >
                        <Input
                            type="date"
                            value={form.data.paid_on}
                            onChange={(event) =>
                                form.setData('paid_on', event.target.value)
                            }
                            required
                        />
                    </Field>
                    <Field label="Reference" error={form.errors.reference}>
                        <Input
                            value={form.data.reference}
                            onChange={(event) =>
                                form.setData('reference', event.target.value)
                            }
                        />
                    </Field>

                    <DialogFooter>
                        <Button type="button" variant="ghost" onClick={onClose}>
                            Cancel
                        </Button>
                        <Button type="submit" disabled={form.processing}>
                            <Banknote className="size-4" />
                            Record payment
                        </Button>
                    </DialogFooter>
                </form>
            </DialogContent>
        </Dialog>
    );
}
