import { Plus, RefreshCw, Trash2, Wand2 } from 'lucide-react';
import { useState } from 'react';
import { Callout } from '@/components/crm/page-header';
import { Button } from '@/components/ui/button';
import { Input } from '@/components/ui/input';
import { suggest as suggestParts } from '@/routes/jobs/parts';
import type { JobPart } from '@/types';

/**
 * One row as the form holds it. Quantities are strings because a number input
 * has to be able to be empty, which is the whole point of "not calculated yet".
 */
type PartRow = {
    key: string;
    category: string | null;
    description: string;
    quantity: string;
    unit: string;
    basis: string;
    needs_review: boolean;
};

const UNITS = ['each', 'pair', 'set', 'pack', 'metre', 'roll', 'box'];

const inputClass =
    'border-input dark:bg-input/30 h-8 w-full rounded-md border bg-transparent px-2 text-sm shadow-xs outline-none focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[2px]';

let sequence = 0;

function toRow(part: Partial<JobPart> & { description: string }): PartRow {
    sequence += 1;

    return {
        key: `part-${sequence}`,
        category: part.category ?? null,
        description: part.description,
        quantity:
            part.quantity === null || part.quantity === undefined
                ? ''
                : String(part.quantity),
        unit: part.unit ?? 'each',
        basis: part.basis ?? '',
        needs_review: part.needs_review ?? false,
    };
}

type Props = {
    /** Rows already saved against the job, for the edit form. */
    initial?: JobPart[];
    /** Name of the design field to read, since the pickers post names. */
    hiddenName?: string;
};

/**
 * The draft parts list for a job.
 *
 * "Suggest parts" reads the design straight off the form it sits in — the
 * system size, panel wattage, chosen inverter and battery — and asks the server
 * to work the list out, so the arithmetic lives in one place rather than being
 * copied into the browser.
 *
 * Everything stays editable. A suggestion is a starting point somebody checks,
 * not an order: descriptions, quantities and units are inputs, rows can be
 * removed, and "Set up parts manually" clears the list for a person who would
 * rather write it themselves.
 *
 * The basis beside each row is the point of the feature. A quantity nobody can
 * see the reasoning for is a quantity nobody can check, so every generated row
 * says where its number came from, and a row the design did not answer is left
 * blank and marked "Needs review" instead of being given a made-up figure.
 */
export function JobPartsPanel({ initial = [] }: Props) {
    const [rows, setRows] = useState<PartRow[]>(() => initial.map(toRow));
    const [generating, setGenerating] = useState(false);
    const [failure, setFailure] = useState<string | null>(null);
    const [warnings, setWarnings] = useState<string[]>([]);
    const [manual, setManual] = useState(false);

    const readField = (name: string): string => {
        const element = document.querySelector<
            HTMLInputElement | HTMLSelectElement | HTMLTextAreaElement
        >(`[name="${name}"]`);

        return element?.value?.trim() ?? '';
    };

    const generate = async () => {
        setGenerating(true);
        setFailure(null);

        const design: Record<string, string> = {};

        for (const name of [
            'site_id',
            'type',
            'installation_config',
            'system_size_kw',
            'panel_count',
            'panel_wattage',
            'panel_make',
            'panel_model',
            'string_count',
            'inverter_make',
            'inverter_model',
            'inverter_capacity_kw',
            'battery_make',
            'battery_model',
            'battery_capacity_kwh',
            'export_limit_kw',
        ]) {
            const value = readField(name);

            if (value !== '') {
                design[name] = value;
            }
        }

        try {
            const response = await fetch(
                `${suggestParts.url()}?${new URLSearchParams(design)}`,
                {
                    headers: { Accept: 'application/json' },
                    credentials: 'same-origin',
                },
            );

            if (!response.ok) {
                throw new Error(`HTTP ${response.status}`);
            }

            const payload = (await response.json()) as {
                items?: Array<Partial<JobPart> & { description: string }>;
                warnings?: string[];
            };

            setRows((payload.items ?? []).map(toRow));
            setWarnings(payload.warnings ?? []);
            setManual(false);
        } catch {
            // A failed request must not look like "this design needs no parts".
            setFailure(
                'The suggested list could not be loaded. Check the connection and try again, or set the list up manually.',
            );
        } finally {
            setGenerating(false);
        }
    };

    const startManually = () => {
        setRows([]);
        setWarnings([]);
        setFailure(null);
        setManual(true);
    };

    const addRow = () => {
        setRows((previous) => [
            ...previous,
            toRow({ description: '', quantity: null, unit: 'each' }),
        ]);
    };

    const update = (key: string, patch: Partial<PartRow>) => {
        setRows((previous) =>
            previous.map((row) => (row.key === key ? { ...row, ...patch } : row)),
        );
    };

    const remove = (key: string) => {
        setRows((previous) => previous.filter((row) => row.key !== key));
    };

    const reviewCount = rows.filter((row) => row.needs_review).length;

    return (
        <div className="flex flex-col gap-4">
            <Callout tone="info" title="A draft list, not a compliance record">
                Quantities and ratings are worked out from what this job records
                and are a starting point only. Check every row against the
                equipment manufacturer&rsquo;s instructions, the site design and
                AS/NZS 5033, 4777.1 and 5139 before ordering or installing.
                Rows marked <strong>Needs review</strong> had nothing to
                calculate from — fill in the design, or set the quantity by
                hand.
            </Callout>

            <div className="flex flex-wrap items-center gap-2">
                <Button
                    type="button"
                    size="sm"
                    onClick={generate}
                    disabled={generating}
                >
                    <Wand2 className="size-3.5" />
                    {generating
                        ? 'Working out the list…'
                        : rows.length > 0
                          ? 'Regenerate from this design'
                          : 'Suggest parts from this design'}
                </Button>
                <Button
                    type="button"
                    size="sm"
                    variant="outline"
                    onClick={startManually}
                >
                    <RefreshCw className="size-3.5" />
                    Set up parts manually
                </Button>
                <Button
                    type="button"
                    size="sm"
                    variant="outline"
                    onClick={addRow}
                >
                    <Plus className="size-3.5" />
                    Add item
                </Button>
                <span className="text-muted-foreground text-xs">
                    {rows.length === 0
                        ? 'No parts yet.'
                        : `${rows.length} item${rows.length === 1 ? '' : 's'}` +
                          (reviewCount > 0
                              ? ` · ${reviewCount} need review`
                              : '')}
                </span>
            </div>

            {failure ? (
                <Callout tone="warning" title="The list could not be drafted">
                    {failure}
                </Callout>
            ) : null}

            {manual ? (
                <p className="text-muted-foreground text-xs">
                    Manual list. Add a row for each item and enter its
                    description, quantity and unit.
                </p>
            ) : null}

            {warnings.length > 0 ? (
                <Callout tone="warning" title="The design is missing something">
                    <ul className="list-disc pl-4">
                        {warnings.map((warning) => (
                            <li key={warning}>{warning}</li>
                        ))}
                    </ul>
                </Callout>
            ) : null}

            {rows.length > 0 ? (
                <div className="overflow-x-auto rounded-lg border border-hairline">
                    <table className="w-full text-sm">
                        <thead className="bg-muted/40 text-muted-foreground text-xs">
                            <tr>
                                <th className="px-3 py-2 text-left font-medium">
                                    Part
                                </th>
                                <th className="w-24 px-3 py-2 text-left font-medium">
                                    Quantity
                                </th>
                                <th className="w-28 px-3 py-2 text-left font-medium">
                                    Unit
                                </th>
                                <th className="px-3 py-2 text-left font-medium">
                                    Basis
                                </th>
                                <th className="w-10 px-3 py-2">
                                    <span className="sr-only">Remove</span>
                                </th>
                            </tr>
                        </thead>
                        <tbody>
                            {rows.map((row, index) => (
                                <tr
                                    key={row.key}
                                    className="border-t border-hairline align-top"
                                >
                                    <td className="px-3 py-2">
                                        <input
                                            type="hidden"
                                            name={`parts[${index}][category]`}
                                            value={row.category ?? ''}
                                        />
                                        <input
                                            type="hidden"
                                            name={`parts[${index}][basis]`}
                                            value={row.basis}
                                        />
                                        <input
                                            type="hidden"
                                            name={`parts[${index}][needs_review]`}
                                            value={row.needs_review ? '1' : '0'}
                                        />
                                        <Input
                                            name={`parts[${index}][description]`}
                                            value={row.description}
                                            onChange={(event) =>
                                                update(row.key, {
                                                    description:
                                                        event.target.value,
                                                })
                                            }
                                            placeholder="What the part is"
                                            aria-label={`Part ${index + 1}`}
                                        />
                                    </td>
                                    <td className="px-3 py-2">
                                        <input
                                            type="number"
                                            min="0"
                                            step="0.01"
                                            name={`parts[${index}][quantity]`}
                                            value={row.quantity}
                                            onChange={(event) =>
                                                update(row.key, {
                                                    quantity: event.target.value,
                                                })
                                            }
                                            placeholder={
                                                row.needs_review
                                                    ? 'To check'
                                                    : 'Qty'
                                            }
                                            className={inputClass}
                                            aria-label={`Quantity for ${row.description || 'part ' + (index + 1)}`}
                                        />
                                    </td>
                                    <td className="px-3 py-2">
                                        <select
                                            name={`parts[${index}][unit]`}
                                            value={row.unit}
                                            onChange={(event) =>
                                                update(row.key, {
                                                    unit: event.target.value,
                                                })
                                            }
                                            className={inputClass}
                                            aria-label={`Unit for ${row.description || 'part ' + (index + 1)}`}
                                        >
                                            {UNITS.map((unit) => (
                                                <option
                                                    key={unit}
                                                    value={unit}
                                                >
                                                    {unit}
                                                </option>
                                            ))}
                                        </select>
                                    </td>
                                    <td className="text-muted-foreground px-3 py-2 text-xs">
                                        {row.basis !== '' ? row.basis : '—'}
                                        {row.needs_review ? (
                                            <span className="ml-2 inline-flex rounded bg-amber-100 px-1.5 py-0.5 text-[10px] font-medium text-amber-900 dark:bg-amber-500/20 dark:text-amber-200">
                                                Needs review
                                            </span>
                                        ) : null}
                                    </td>
                                    <td className="px-3 py-2">
                                        <button
                                            type="button"
                                            onClick={() => remove(row.key)}
                                            className="text-muted-foreground hover:text-destructive"
                                            aria-label={`Remove ${row.description || 'part ' + (index + 1)}`}
                                        >
                                            <Trash2 className="size-4" />
                                        </button>
                                    </td>
                                </tr>
                            ))}
                        </tbody>
                    </table>
                </div>
            ) : (
                <p className="text-muted-foreground text-sm">
                    Nothing on the list yet. Suggest a list from the design, or
                    set one up by hand.
                </p>
            )}
        </div>
    );
}
