import { useEffect, useMemo, useState } from 'react';
import { FormField } from '@/components/crm/native-select';
import { Input } from '@/components/ui/input';
import { equipment as equipmentLookup } from '@/routes/lookups';

export type EquipmentKind = 'panel' | 'inverter' | 'battery';

type CatalogueModel = {
    model: string;
    name: string;
    capacity: string | null;
};

type CatalogueBrand = {
    brand: string;
    models: CatalogueModel[];
};

type Props = {
    /** Which line of the system this pair describes. */
    kind: EquipmentKind;
    /** Prefix for the two labels: "Panel", "Inverter", "Battery". */
    label: string;
    /** Field names the form posts, unchanged from the plain text versions. */
    makeName: string;
    modelName: string;
    defaultMake?: string;
    defaultModel?: string;
    makeError?: string;
    modelError?: string;
};

/**
 * The brand and model of one piece of equipment, offered from the warehouse
 * catalogue.
 *
 * The catalogue is filled from Solar Link Australia's storefront, so the list
 * is the range the business actually orders: pick a brand and the models narrow
 * to that maker, pick a model and the brand fills in, and both fields still
 * accept anything typed by hand. That last part matters because the panels and
 * inverters already installed across thousands of imported jobs are not all in
 * any supplier's current catalogue.
 *
 * This is a datalist rather than a searchable picker because the value posted
 * is the name itself, not an id: the job stores "Sungrow" and "SH5.0RS", so the
 * form keeps submitting exactly what it always did.
 */
export function EquipmentFields({
    kind,
    label,
    makeName,
    modelName,
    defaultMake = '',
    defaultModel = '',
    makeError,
    modelError,
}: Props) {
    const [brands, setBrands] = useState<CatalogueBrand[]>([]);
    const [failed, setFailed] = useState(false);
    const [brand, setBrand] = useState(defaultMake);
    const [model, setModel] = useState(defaultModel);

    useEffect(() => {
        const controller = new AbortController();

        fetch(equipmentLookup.url({ query: { kind } }), {
            headers: { Accept: 'application/json' },
            signal: controller.signal,
        })
            .then((response) =>
                response.ok
                    ? (response.json() as Promise<{
                          brands?: CatalogueBrand[];
                      }>)
                    : Promise.reject(new Error(String(response.status))),
            )
            .then((payload) => {
                if (!controller.signal.aborted) {
                    setBrands(payload.brands ?? []);
                }
            })
            .catch(() => {
                if (!controller.signal.aborted) {
                    setFailed(true);
                }
            });

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

    const brandNames = useMemo(
        () => brands.map((entry) => entry.brand),
        [brands],
    );

    // A brand that is not in the catalogue has no model list of its own, so
    // every model is offered instead and choosing one settles the brand.
    const chosen =
        brands.find(
            (entry) => entry.brand.toLowerCase() === brand.trim().toLowerCase(),
        ) ?? null;

    const models = useMemo(
        () =>
            chosen === null
                ? brands.flatMap((entry) => entry.models)
                : chosen.models,
        [brands, chosen],
    );

    const handleModelChange = (value: string) => {
        setModel(value);

        const picked = brands.find((entry) =>
            entry.models.some((option) => option.model === value),
        );

        if (picked !== undefined && picked.brand !== brand) {
            setBrand(picked.brand);
        }
    };

    const brandListId = `${makeName}-brands`;
    const modelListId = `${modelName}-models`;

    return (
        <>
            <FormField
                label={`${label} brand`}
                htmlFor={makeName}
                error={makeError}
            >
                <Input
                    id={makeName}
                    name={makeName}
                    list={brandListId}
                    autoComplete="off"
                    value={brand}
                    onChange={(event) => setBrand(event.target.value)}
                />
                <datalist id={brandListId}>
                    {brandNames.map((name) => (
                        <option key={name} value={name} />
                    ))}
                </datalist>
            </FormField>

            <FormField
                label={`${label} model`}
                htmlFor={modelName}
                error={modelError}
                hint={
                    failed
                        ? 'The catalogue is unavailable right now — enter the brand and model by hand.'
                        : 'The catalogue offers Solar Link Australia’s range; anything else can still be typed.'
                }
            >
                <Input
                    id={modelName}
                    name={modelName}
                    list={modelListId}
                    autoComplete="off"
                    value={model}
                    onChange={(event) => handleModelChange(event.target.value)}
                />
                <datalist id={modelListId}>
                    {models.map((option) => (
                        <option
                            key={`${option.model}-${option.name}`}
                            value={option.model}
                        >
                            {option.capacity === null
                                ? option.name
                                : `${option.name} · ${option.capacity}`}
                        </option>
                    ))}
                </datalist>
            </FormField>
        </>
    );
}
