import { Head, Link, router, useForm } from '@inertiajs/react';
import {
    Banknote,
    CheckCircle2,
    ClipboardCheck,
    PencilLine,
    Plus,
    Save,
    Upload,
    Wallet,
} from 'lucide-react';
import { useEffect, useMemo, useRef, useState } from 'react';
import { Combobox, type ComboboxOption } from '@/components/crm/combobox';
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,
    toDateInputValue,
} from '@/lib/format';
import { show as customerShow } from '@/routes/customers';
import {
    decide,
    index as reimbursementsIndex,
    pay,
    store,
    update as reimbursementsUpdate,
} from '@/routes/finance/reimbursements';
import {
    customers as customersLookup,
    jobs as jobsLookup,
    tickets as ticketsLookup,
} from '@/routes/lookups';
import { show as ticketShow } from '@/routes/tickets';
import type { Paginated } from '@/types';

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

type ClaimRow = {
    id: number;
    reference: string;
    claimant: { id: number; name: string };
    reviewer: { id: number; name: string } | null;
    job: { id: number; job_number: string } | null;
    customer: {
        id: number;
        name: string;
        customer_number: string | null;
    } | null;
    ticket: {
        id: number;
        number: string;
        issue: string;
        status: StatusOption;
        job_id: number | null;
    } | null;
    installer: { id: number; name: string } | null;
    system_replaced: string | null;
    invoice_status: StatusOption | null;
    invoice_number: string | null;
    resolved_on: string | null;
    is_resolved: boolean;
    category: string;
    category_value: string;
    expense_date: string;
    description: string;
    amount: string;
    gst_amount: string | null;
    approved_amount: string | null;
    receipt_reference: string | null;
    status: StatusOption;
    submitted_at: string | null;
    reviewed_at: string | null;
    decision_notes: string | null;
    rejection_reason: string | null;
    paid_on: string | null;
    can_edit: boolean;
};

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

type CustomerOption = ComboboxOption;

type JobOption = {
    value: number;
    label: string;
    customer_id: number;
    installer_id: number | null;
    installer: string | null;
    status: string;
};

type TicketOption = ComboboxOption & {
    status: string;
    status_color: string;
    resolved: boolean;
    job_id: number | null;
    job: string | null;
    /** The crew on the register who did the work, and the id to carry across. */
    installer_id: number | null;
    installer: string | null;
};

type StaffOption = { value: number; label: string; role: string | null };

const textareaClassName =
    'w-full rounded-md border border-input bg-transparent px-3 py-2 text-sm outline-none focus-visible:border-ring focus-visible:ring-[3px] focus-visible:ring-ring/50';

/**
 * The form a claim is filed with, and the one used later to record what the
 * invoice and the fault did. Both are the same question — "what was this money
 * for?" — so they are the same form.
 */
function ClaimDialog({
    open,
    claim,
    options,
    onClose,
}: {
    open: boolean;
    claim: ClaimRow | null;
    options: {
        categories: Array<{ value: string; label: string }>;
        invoiceStatuses: StatusOption[];
        installers: ComboboxOption[];
    };
    onClose: () => void;
}) {
    const editing = claim !== null;
    const today = toDateInputValue(new Date().toISOString());

    const form = useForm({
        expense_date: claim?.expense_date ?? today,
        category:
            claim?.category_value ?? options.categories[0]?.value ?? 'other',
        customer_id: claim?.customer?.id ?? (null as number | null),
        ticket_id: claim?.ticket?.id ?? (null as number | null),
        job_id: claim?.job?.id ?? (null as number | null),
        installer_id: claim?.installer?.id ?? (null as number | null),
        system_replaced: claim?.system_replaced ?? '',
        invoice_status: claim?.invoice_status?.value ?? '',
        invoice_number: claim?.invoice_number ?? '',
        resolved_on: claim?.resolved_on ?? '',
        description: claim?.description ?? '',
        amount: claim?.amount ?? '',
        gst_amount: claim?.gst_amount ?? '',
        receipt_reference: claim?.receipt_reference ?? '',
    });

    // The customer list runs to thousands of rows, so it is searched on the
    // server; the chosen customer is kept in the list so the field keeps its
    // name after the search is over.
    const [customerOptions, setCustomerOptions] = useState<CustomerOption[]>(
        () =>
            claim?.customer
                ? [
                      {
                          value: claim.customer.id,
                          label: claim.customer.name,
                          note: claim.customer.customer_number,
                      },
                  ]
                : [],
    );

    // A ticket is only offered once a customer is named, and only from that
    // customer's own history: a claim can never point at somebody else's fault.
    const [ticketOptions, setTicketOptions] = useState<TicketOption[]>(() =>
        claim?.ticket
            ? [
                  {
                      value: claim.ticket.id,
                      label: `${claim.ticket.number} · ${claim.ticket.issue}`,
                      note: claim.ticket.status.label,
                      meta: claim.ticket.status.label,
                      status: claim.ticket.status.label,
                      status_color: claim.ticket.status.color,
                      resolved: false,
                      job_id: claim.ticket.job_id,
                      job: claim.job?.job_number ?? null,
                      installer_id: claim.installer?.id ?? null,
                      installer: claim.installer?.name ?? null,
                  },
              ]
            : [],
    );
    const [ticketCount, setTicketCount] = useState<number | null>(null);
    const [loadingTickets, setLoadingTickets] = useState(false);

    // The customer's jobs, fetched when the customer is named, so the job picker
    // covers their whole history rather than the newest few hundred jobs.
    const [jobOptions, setJobOptions] = useState<JobOption[]>([]);
    const [loadingJobs, setLoadingJobs] = useState(false);

    // The effect that fills the form from a single ticket needs to read the
    // current values without re-running every time one of them changes.
    const dataRef = useRef(form.data);

    useEffect(() => {
        dataRef.current = form.data;
    }, [form.data]);

    const customerId = form.data.customer_id;
    const ticketId = form.data.ticket_id;
    const jobId = form.data.job_id;

    useEffect(() => {
        if (customerId === null) {
            setTicketOptions([]);
            setJobOptions([]);
            setTicketCount(null);
            setLoadingTickets(false);
            setLoadingJobs(false);

            return;
        }

        const controller = new AbortController();
        setLoadingTickets(true);
        setLoadingJobs(true);

        fetch(`${ticketsLookup().url}?customer=${customerId}`, {
            headers: { Accept: 'application/json' },
            signal: controller.signal,
        })
            .then((response) =>
                response.ok
                    ? (response.json() as Promise<{ options?: TicketOption[] }>)
                    : { options: [] },
            )
            .then((payload) => {
                if (controller.signal.aborted) {
                    return;
                }

                const listed = payload.options ?? [];

                setTicketOptions(listed);
                setTicketCount(listed.length);

                // One ticket on the account is almost always the fault the
                // money was spent on. When there are several, somebody has to
                // say which one rather than have the newest guessed for them.
                if (dataRef.current.ticket_id === null && listed.length === 1) {
                    const only = listed[0];

                    form.setData({
                        ...dataRef.current,
                        ticket_id: only.value,
                        job_id: dataRef.current.job_id ?? only.job_id,
                        resolved_on:
                            dataRef.current.resolved_on ||
                            (only.resolved ? today : ''),
                    });
                }
            })
            .catch(() => {
                // A cancelled request is the normal case when the customer is
                // changed twice in a row, and the picker simply stays empty.
            })
            .finally(() => {
                if (!controller.signal.aborted) {
                    setLoadingTickets(false);
                }
            });

        fetch(`${jobsLookup().url}?customer=${customerId}`, {
            headers: { Accept: 'application/json' },
            signal: controller.signal,
        })
            .then((response) =>
                response.ok
                    ? (response.json() as Promise<{ options?: JobOption[] }>)
                    : { options: [] },
            )
            .then((payload) => {
                if (!controller.signal.aborted) {
                    setJobOptions(payload.options ?? []);
                }
            })
            .catch(() => {
                // Same as above: a cancelled lookup is not a failure.
            })
            .finally(() => {
                if (!controller.signal.aborted) {
                    setLoadingJobs(false);
                }
            });

        return () => controller.abort();
    }, [customerId]);

    /**
     * Naming the customer restarts the job and ticket pickers. Whatever was
     * chosen from the previous customer no longer describes this one.
     */
    const handleCustomerChange = (
        nextId: number | null,
        option: ComboboxOption | null,
    ) => {
        const chosen = option as CustomerOption | null;

        if (chosen !== null) {
            setCustomerOptions((previous) =>
                previous.some((customer) => customer.value === chosen.value)
                    ? previous
                    : [chosen, ...previous],
            );
        }

        form.setData({
            ...form.data,
            customer_id: nextId,
            ticket_id: null,
            job_id: null,
            installer_id: null,
        });
    };

    /**
     * Choosing the ticket fills in the rest of the work: its job, whether the
     * fault is already resolved, and the crew who did it. Everything stays
     * editable, because the claim may know better than the ticket.
     *
     * The installer is the ticket's crew when it has one — the register row the
     * claim is paid about, the same record a ticket names. When the ticket has
     * nobody recorded, the field is left for a search of the register.
     */
    const handleTicketChange = (
        nextId: number | null,
        option: ComboboxOption | null,
    ) => {
        const chosen = option as TicketOption | null;

        if (chosen !== null) {
            setTicketOptions((previous) =>
                previous.some((ticket) => ticket.value === chosen.value)
                    ? previous
                    : [chosen, ...previous],
            );
        }

        form.setData({
            ...form.data,
            ticket_id: nextId,
            job_id: chosen?.job_id ?? form.data.job_id,
            installer_id: chosen?.installer_id ?? form.data.installer_id,
            resolved_on:
                chosen === null
                    ? form.data.resolved_on
                    : form.data.resolved_on || (chosen.resolved ? today : ''),
        });
    };

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

        if (claim !== null) {
            form.patch(reimbursementsUpdate(claim.id).url, callbacks);

            return;
        }

        form.post(store().url, callbacks);
    };

    const ticketChoices = useMemo(
        () =>
            ticketOptions.map((ticket) => ({
                value: ticket.value,
                label: ticket.label,
                note: [ticket.status, ticket.job, ticket.installer]
                    .filter(Boolean)
                    .join(' · '),
                meta: ticket.resolved ? 'Resolved' : 'Open',
            })),
        [ticketOptions],
    );

    const jobChoices = useMemo(() => {
        const listed = jobOptions.map((job) => ({
            value: job.value,
            label: job.label,
            note: job.installer
                ? `Installed by ${job.installer}`
                : 'No installer on this job yet',
            meta: job.status,
        }));

        // A job taken from a ticket, or one already on the claim, is kept on
        // offer even when it falls outside the customer's fetched list — the
        // field must never show "not job related" while a job is linked.
        if (jobId !== null && !listed.some((job) => job.value === jobId)) {
            listed.unshift({
                value: jobId,
                label:
                    claim?.job?.id === jobId
                        ? claim.job.job_number
                        : (ticketOptions.find(
                              (ticket) => ticket.job_id === jobId,
                          )?.job ?? `Job #${jobId}`),
                note: '',
                meta: '',
            });
        }

        return listed;
    }, [jobOptions, jobId, claim, ticketOptions]);

    const chosenJob =
        jobId === null
            ? null
            : (jobOptions.find((job) => job.value === jobId) ?? null);

    // The ticket whose crew the installer field was filled from, so the hint can
    // say whose name it is carrying.
    const chosenTicket =
        ticketId === null
            ? null
            : (ticketOptions.find((ticket) => ticket.value === ticketId) ??
              null);

    return (
        <Dialog open={open} onOpenChange={(state) => !state && onClose()}>
            <DialogContent className="max-h-[90vh] overflow-y-auto sm:max-w-2xl">
                <DialogHeader>
                    <DialogTitle>
                        {editing ? `Edit ${claim.reference}` : 'New claim'}
                    </DialogTitle>
                    <DialogDescription>
                        What you spent, which customer's system it was for —
                        and, if a ticket reported the fault, which one.
                    </DialogDescription>
                </DialogHeader>

                <form
                    onSubmit={(event) => {
                        event.preventDefault();
                        submit();
                    }}
                    className="grid gap-3 sm:grid-cols-2"
                >
                    <Field
                        label="Date"
                        error={form.errors.expense_date}
                        required
                    >
                        <Input
                            type="date"
                            value={form.data.expense_date}
                            onChange={(event) =>
                                form.setData('expense_date', event.target.value)
                            }
                            required
                        />
                    </Field>
                    <Field
                        label="Category"
                        error={form.errors.category}
                        required
                    >
                        <NativeSelect
                            value={form.data.category}
                            onChange={(event) =>
                                form.setData('category', event.target.value)
                            }
                        >
                            {options.categories.map((category) => (
                                <option
                                    key={category.value}
                                    value={category.value}
                                >
                                    {category.label}
                                </option>
                            ))}
                        </NativeSelect>
                    </Field>
                    <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="Receipt reference"
                        error={form.errors.receipt_reference}
                        hint="Docket or invoice number"
                    >
                        <Input
                            value={form.data.receipt_reference}
                            onChange={(event) =>
                                form.setData(
                                    'receipt_reference',
                                    event.target.value,
                                )
                            }
                        />
                    </Field>

                    <Field
                        label="Client"
                        error={form.errors.customer_id}
                        hint="Who the work was for"
                    >
                        <Combobox
                            id="claim-customer"
                            name="customer_id"
                            options={customerOptions}
                            value={customerId}
                            onChange={handleCustomerChange}
                            placeholder="Search the customer database"
                            clearLabel="Not customer related"
                            remote={{
                                url: customersLookup().url,
                                queryParam: 'search',
                                minChars: 1,
                            }}
                        />
                    </Field>
                    <Field
                        label="Ticket"
                        error={form.errors.ticket_id}
                        hint={
                            ticketCount !== null && ticketCount > 1
                                ? `${ticketCount} tickets on this client — pick the one this claim is for`
                                : undefined
                        }
                    >
                        <Combobox
                            id="claim-ticket"
                            name="ticket_id"
                            options={ticketChoices}
                            value={ticketId}
                            onChange={handleTicketChange}
                            placeholder={
                                customerId === null
                                    ? 'Choose the client first'
                                    : loadingTickets
                                      ? 'Loading tickets…'
                                      : 'Search this client’s tickets'
                            }
                            clearLabel="No ticket"
                            emptyLabel={
                                customerId === null
                                    ? 'Choose the client first'
                                    : 'No tickets on this client'
                            }
                            disabled={customerId === null}
                        />
                    </Field>

                    <Field
                        label="Job"
                        error={form.errors.job_id}
                        hint={
                            chosenJob
                                ? `Installed by ${chosenJob.installer ?? 'nobody recorded'}`
                                : 'Connected to the ticket where it has one'
                        }
                    >
                        <NativeSelect
                            value={jobId ?? ''}
                            onChange={(event) =>
                                form.setData({
                                    ...form.data,
                                    job_id:
                                        event.target.value === ''
                                            ? null
                                            : Number(event.target.value),
                                })
                            }
                            disabled={customerId === null || loadingJobs}
                        >
                            <option value="">
                                {customerId === null
                                    ? 'Choose the client first'
                                    : 'Not job related'}
                            </option>
                            {jobChoices.map((job) => (
                                <option key={job.value} value={job.value}>
                                    {job.label}
                                </option>
                            ))}
                        </NativeSelect>
                    </Field>
                    <Field
                        label="Installer"
                        error={form.errors.installer_id}
                        hint={
                            chosenTicket?.installer_id
                                ? `Fitted by ${chosenTicket.installer ?? 'the crew on the ticket'}`
                                : 'Who fitted the replacement — search the register'
                        }
                    >
                        <Combobox
                            id="claim-installer"
                            name="installer_id"
                            options={options.installers}
                            value={form.data.installer_id}
                            onChange={(next) =>
                                form.setData('installer_id', next)
                            }
                            placeholder="Search the installer register"
                            clearLabel="Not recorded"
                            emptyLabel="No crew in the register matches that"
                        />
                    </Field>

                    <Field
                        label="System replaced"
                        error={form.errors.system_replaced}
                        hint="e.g. Sungrow SH5.0RS inverter"
                    >
                        <Input
                            value={form.data.system_replaced}
                            onChange={(event) =>
                                form.setData(
                                    'system_replaced',
                                    event.target.value,
                                )
                            }
                            placeholder="What was swapped out"
                        />
                    </Field>
                    <Field
                        label="Invoice status"
                        error={form.errors.invoice_status}
                        hint="Was the replacement billed?"
                    >
                        <NativeSelect
                            value={form.data.invoice_status}
                            onChange={(event) =>
                                form.setData(
                                    'invoice_status',
                                    event.target.value,
                                )
                            }
                        >
                            <option value="">Not recorded</option>
                            {options.invoiceStatuses.map((status) => (
                                <option key={status.value} value={status.value}>
                                    {status.label}
                                </option>
                            ))}
                        </NativeSelect>
                    </Field>

                    <Field
                        label="Invoice number"
                        error={form.errors.invoice_number}
                        hint="Where it was billed"
                    >
                        <Input
                            value={form.data.invoice_number}
                            onChange={(event) =>
                                form.setData(
                                    'invoice_number',
                                    event.target.value,
                                )
                            }
                        />
                    </Field>
                    <Field
                        label="Resolved"
                        error={form.errors.resolved_on}
                        hint="The fault is settled, not just paid"
                    >
                        <div className="flex flex-wrap items-center gap-3">
                            <label className="flex items-center gap-2 text-sm">
                                <input
                                    type="checkbox"
                                    className="size-4 rounded border-input"
                                    checked={form.data.resolved_on !== ''}
                                    onChange={(event) =>
                                        form.setData(
                                            'resolved_on',
                                            event.target.checked ? today : '',
                                        )
                                    }
                                />
                                This issue is resolved
                            </label>
                            {form.data.resolved_on !== '' ? (
                                <Input
                                    type="date"
                                    className="h-9 w-44"
                                    value={form.data.resolved_on}
                                    max={today}
                                    onChange={(event) =>
                                        form.setData(
                                            'resolved_on',
                                            event.target.value,
                                        )
                                    }
                                />
                            ) : null}
                        </div>
                    </Field>

                    <Field
                        label="Description"
                        error={form.errors.description}
                        required
                        className="sm:col-span-2"
                    >
                        <textarea
                            rows={3}
                            value={form.data.description}
                            onChange={(event) =>
                                form.setData('description', event.target.value)
                            }
                            className={textareaClassName}
                            required
                        />
                    </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" />
                            {editing ? 'Save changes' : 'Submit claim'}
                        </Button>
                    </DialogFooter>
                </form>
            </DialogContent>
        </Dialog>
    );
}

export default function Reimbursements({
    claims,
    filters,
    options,
    stats,
    can,
}: {
    claims: Paginated<ClaimRow>;
    filters: Filters;
    options: {
        categories: Array<{ value: string; label: string }>;
        reimbursementStatuses: StatusOption[];
        reimbursementInvoiceStatuses: StatusOption[];
        staff: StaffOption[];
        installers: ComboboxOption[];
    };
    stats: {
        awaiting_review: number;
        awaiting_review_value: number;
        awaiting_payment: number;
        awaiting_payment_value: number;
        paid_this_month: number;
    };
    can: { submit: boolean; approve: boolean };
}) {
    const [search, setSearch] = useState(filters.search ?? '');
    const [showCreate, setShowCreate] = useState(false);
    const [editing, setEditing] = useState<ClaimRow | null>(null);
    const [reviewing, setReviewing] = useState<ClaimRow | null>(null);
    const [paying, setPaying] = useState<ClaimRow | null>(null);

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

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

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

            <div className="flex flex-col gap-5">
                <PageHeader
                    eyebrow={
                        <>
                            <Wallet className="size-3.5 text-solar" />
                            <span>Finance</span>
                        </>
                    }
                    title="Reimbursements"
                    description="Money staff have spent on the company's behalf: installers' parts, fuel, permits. Each claim can name the client, the ticket that reported the fault and the job behind it, so finance can see what was replaced, who fitted it, whether it was invoiced and whether it is settled."
                    actions={
                        can.submit ? (
                            <Button
                                size="sm"
                                onClick={() => setShowCreate(true)}
                            >
                                <Plus className="size-4" />
                                New claim
                            </Button>
                        ) : undefined
                    }
                />

                <div className="grid gap-3 sm:grid-cols-2 xl:grid-cols-4">
                    <KpiTile
                        label="Awaiting review"
                        value={stats.awaiting_review}
                        hint={formatCompactCurrency(
                            stats.awaiting_review_value,
                        )}
                        icon={ClipboardCheck}
                        tone={stats.awaiting_review > 0 ? 'warning' : 'slate'}
                        href={
                            reimbursementsIndex({
                                query: { scope: 'awaiting_review' },
                            }).url
                        }
                    />
                    <KpiTile
                        label="Awaiting payment"
                        value={stats.awaiting_payment}
                        hint={formatCompactCurrency(
                            stats.awaiting_payment_value,
                        )}
                        icon={Banknote}
                        tone="blue"
                        href={
                            reimbursementsIndex({
                                query: { scope: 'awaiting_payment' },
                            }).url
                        }
                    />
                    <KpiTile
                        label="Paid this month"
                        value={formatCompactCurrency(stats.paid_this_month)}
                        hint="Already in people's accounts"
                        icon={CheckCircle2}
                        tone="success"
                        href={
                            reimbursementsIndex({ query: { scope: 'paid' } })
                                .url
                        }
                    />
                    <KpiTile
                        label="My claims"
                        value="Open"
                        hint="Everything you have submitted"
                        icon={Upload}
                        tone="violet"
                        href={
                            reimbursementsIndex({ query: { scope: 'mine' } })
                                .url
                        }
                    />
                </div>

                <form
                    onSubmit={(event) => {
                        event.preventDefault();
                        apply({});
                    }}
                >
                    <FilterBar
                        actions={
                            <SegmentedControl
                                value={filters.scope}
                                onChange={(scope) => apply({ scope })}
                                options={[
                                    {
                                        value: 'awaiting_review',
                                        label: 'To review',
                                    },
                                    {
                                        value: 'awaiting_payment',
                                        label: 'To pay',
                                    },
                                    { value: 'paid', label: 'Paid' },
                                    { value: 'mine', label: 'Mine' },
                                    { value: 'open', label: 'Open' },
                                    { value: 'all', label: 'All' },
                                ]}
                            />
                        }
                    >
                        <SearchInput
                            value={search}
                            onChange={setSearch}
                            placeholder="Reference, person, client, ticket or system"
                        />
                        <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.reimbursementStatuses.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>Claim</TableHead>
                                    <TableHead>Client / ticket</TableHead>
                                    <TableHead>Work</TableHead>
                                    <TableHead>Invoice</TableHead>
                                    <TableHead>Resolved</TableHead>
                                    <TableHead>Status</TableHead>
                                    <TableHead className="text-right">
                                        Claimed
                                    </TableHead>
                                    <TableHead className="text-right">
                                        Approved
                                    </TableHead>
                                    {can.approve || can.submit ? (
                                        <TableHead />
                                    ) : null}
                                </TableRow>
                            </TableHeader>
                            <TableBody>
                                {claims.data.length === 0 ? (
                                    <TableRow>
                                        <TableCell
                                            colSpan={
                                                can.approve || can.submit
                                                    ? 9
                                                    : 8
                                            }
                                            className="px-4 py-10 text-center text-sm text-muted-foreground"
                                        >
                                            No claims match these filters.
                                        </TableCell>
                                    </TableRow>
                                ) : null}
                                {claims.data.map((claim) => (
                                    <TableRow key={claim.id}>
                                        <TableCell>
                                            <span className="block font-mono text-xs">
                                                {claim.reference}
                                            </span>
                                            <span className="block text-[11px] text-muted-foreground">
                                                {formatDate(claim.expense_date)}{' '}
                                                · {claim.claimant.name}
                                            </span>
                                            <span className="block text-[11px] text-muted-foreground">
                                                {claim.category}
                                                {claim.job
                                                    ? ` · ${claim.job.job_number}`
                                                    : ''}
                                            </span>
                                        </TableCell>
                                        <TableCell className="text-xs">
                                            {claim.customer ? (
                                                <Link
                                                    href={
                                                        customerShow(
                                                            claim.customer.id,
                                                        ).url
                                                    }
                                                    className="block font-medium hover:underline"
                                                >
                                                    {claim.customer.name}
                                                </Link>
                                            ) : (
                                                <span className="block text-muted-foreground">
                                                    No client linked
                                                </span>
                                            )}
                                            {claim.ticket ? (
                                                <Link
                                                    href={
                                                        ticketShow(
                                                            claim.ticket.id,
                                                        ).url
                                                    }
                                                    className="block text-[11px] text-muted-foreground hover:underline"
                                                >
                                                    {claim.ticket.number} ·{' '}
                                                    {claim.ticket.status.label}
                                                </Link>
                                            ) : null}
                                        </TableCell>
                                        <TableCell className="text-xs">
                                            {claim.system_replaced ? (
                                                <span className="block">
                                                    {claim.system_replaced}
                                                </span>
                                            ) : (
                                                <span className="block text-muted-foreground">
                                                    —
                                                </span>
                                            )}
                                            <span className="block text-[11px] text-muted-foreground">
                                                {claim.installer
                                                    ? `Fitted by ${claim.installer.name}`
                                                    : 'Installer not recorded'}
                                            </span>
                                        </TableCell>
                                        <TableCell className="text-xs">
                                            {claim.invoice_status ? (
                                                <StatusLabel
                                                    tone={
                                                        claim.invoice_status
                                                            .color
                                                    }
                                                >
                                                    {claim.invoice_status.label}
                                                </StatusLabel>
                                            ) : (
                                                <span className="text-muted-foreground">
                                                    —
                                                </span>
                                            )}
                                            {claim.invoice_number ? (
                                                <span className="mt-1 block text-[11px] text-muted-foreground">
                                                    {claim.invoice_number}
                                                </span>
                                            ) : null}
                                        </TableCell>
                                        <TableCell className="text-xs">
                                            {claim.is_resolved ? (
                                                <>
                                                    <StatusLabel tone="success">
                                                        Resolved
                                                    </StatusLabel>
                                                    <span className="mt-1 block text-[11px] text-muted-foreground">
                                                        {formatDate(
                                                            claim.resolved_on,
                                                        )}
                                                    </span>
                                                </>
                                            ) : (
                                                <StatusLabel tone="warning">
                                                    Open
                                                </StatusLabel>
                                            )}
                                        </TableCell>
                                        <TableCell>
                                            <StatusLabel
                                                tone={claim.status.color}
                                            >
                                                {claim.status.label}
                                            </StatusLabel>
                                        </TableCell>
                                        <TableCell className="text-right text-sm crm-num">
                                            {formatCurrency(
                                                Number(claim.amount),
                                            )}
                                        </TableCell>
                                        <TableCell className="text-right text-sm crm-num">
                                            {claim.approved_amount === null
                                                ? '—'
                                                : formatCurrency(
                                                      Number(
                                                          claim.approved_amount,
                                                      ),
                                                  )}
                                        </TableCell>
                                        {can.approve || can.submit ? (
                                            <TableCell className="text-right">
                                                <div className="flex items-center justify-end gap-1">
                                                    {claim.can_edit ? (
                                                        <Button
                                                            size="sm"
                                                            variant="ghost"
                                                            onClick={() =>
                                                                setEditing(
                                                                    claim,
                                                                )
                                                            }
                                                            title="Update the service details"
                                                        >
                                                            <PencilLine className="size-3.5" />
                                                            Edit
                                                        </Button>
                                                    ) : null}
                                                    {can.approve ? (
                                                        claim.status.value ===
                                                            'submitted' ||
                                                        claim.status.value ===
                                                            'under_review' ||
                                                        claim.status.value ===
                                                            'information_required' ? (
                                                            <Button
                                                                size="sm"
                                                                variant="ghost"
                                                                onClick={() =>
                                                                    setReviewing(
                                                                        claim,
                                                                    )
                                                                }
                                                            >
                                                                Review
                                                            </Button>
                                                        ) : claim.status
                                                              .value ===
                                                              'approved' ||
                                                          claim.status.value ===
                                                              'awaiting_payment' ? (
                                                            <Button
                                                                size="sm"
                                                                variant="ghost"
                                                                onClick={() =>
                                                                    setPaying(
                                                                        claim,
                                                                    )
                                                                }
                                                            >
                                                                Mark paid
                                                            </Button>
                                                        ) : null
                                                    ) : null}
                                                </div>
                                            </TableCell>
                                        ) : null}
                                    </TableRow>
                                ))}
                            </TableBody>
                        </Table>
                    </div>
                    <DataPagination page={claims} noun="claims" />
                </TableCard>
            </div>

            {can.submit ? (
                <ClaimDialog
                    open={showCreate}
                    claim={null}
                    options={{
                        categories: options.categories,
                        invoiceStatuses: options.reimbursementInvoiceStatuses,
                        installers: options.installers,
                    }}
                    onClose={() => setShowCreate(false)}
                />
            ) : null}

            {editing ? (
                <ClaimDialog
                    open
                    claim={editing}
                    options={{
                        categories: options.categories,
                        invoiceStatuses: options.reimbursementInvoiceStatuses,
                        installers: options.installers,
                    }}
                    onClose={() => setEditing(null)}
                />
            ) : null}

            {reviewing ? (
                <ReviewDialog
                    claim={reviewing}
                    onClose={() => setReviewing(null)}
                />
            ) : null}

            {paying ? (
                <PaidDialog claim={paying} onClose={() => setPaying(null)} />
            ) : null}
        </>
    );
}

function ReviewDialog({
    claim,
    onClose,
}: {
    claim: ClaimRow;
    onClose: () => void;
}) {
    const form = useForm({
        decision: 'approve',
        approved_amount: claim.amount,
        notes: '',
    });

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

    return (
        <Dialog open onOpenChange={(state) => !state && onClose()}>
            <DialogContent className="sm:max-w-md">
                <DialogHeader>
                    <DialogTitle>Review {claim.reference}</DialogTitle>
                    <DialogDescription>
                        {claim.claimant.name} claimed{' '}
                        {formatCurrency(Number(claim.amount))} on{' '}
                        {formatDate(claim.expense_date)}.
                    </DialogDescription>
                </DialogHeader>

                <form
                    onSubmit={(event) => {
                        event.preventDefault();
                        submit();
                    }}
                    className="grid gap-3"
                >
                    <Field label="Decision" error={form.errors.decision}>
                        <NativeSelect
                            value={form.data.decision}
                            onChange={(event) =>
                                form.setData('decision', event.target.value)
                            }
                        >
                            <option value="approve">Approve</option>
                            <option value="reject">Reject</option>
                            <option value="request_information">
                                Request information
                            </option>
                        </NativeSelect>
                    </Field>
                    {form.data.decision === 'approve' ? (
                        <Field
                            label="Approved amount"
                            error={form.errors.approved_amount}
                        >
                            <Input
                                type="number"
                                min={0}
                                step="0.01"
                                value={form.data.approved_amount}
                                onChange={(event) =>
                                    form.setData(
                                        'approved_amount',
                                        event.target.value,
                                    )
                                }
                            />
                        </Field>
                    ) : null}
                    <Field label="Notes" error={form.errors.notes}>
                        <textarea
                            rows={3}
                            value={form.data.notes}
                            onChange={(event) =>
                                form.setData('notes', event.target.value)
                            }
                            className={textareaClassName}
                        />
                    </Field>

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

function PaidDialog({
    claim,
    onClose,
}: {
    claim: ClaimRow;
    onClose: () => void;
}) {
    const form = useForm({
        paid_on: new Date().toISOString().slice(0, 10),
        payment_reference: '',
    });

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

    return (
        <Dialog open onOpenChange={(state) => !state && onClose()}>
            <DialogContent className="sm:max-w-md">
                <DialogHeader>
                    <DialogTitle>Mark {claim.reference} paid</DialogTitle>
                    <DialogDescription>
                        Paying{' '}
                        {formatCurrency(
                            Number(claim.approved_amount ?? claim.amount),
                        )}{' '}
                        to {claim.claimant.name}.
                    </DialogDescription>
                </DialogHeader>

                <form
                    onSubmit={(event) => {
                        event.preventDefault();
                        submit();
                    }}
                    className="grid gap-3"
                >
                    <Field label="Paid 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="Payment reference"
                        error={form.errors.payment_reference}
                    >
                        <Input
                            value={form.data.payment_reference}
                            onChange={(event) =>
                                form.setData(
                                    'payment_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>
    );
}
