import { router } from '@inertiajs/react';
import { useCallback, useEffect, useMemo, useRef, useState } from 'react';

import { Checkbox } from '@/components/ui/checkbox';
import {
    Dialog,
    DialogContent,
    DialogDescription,
    DialogHeader,
    DialogTitle,
} from '@/components/ui/dialog';
import { index as zipCodesIndex, sync as zipCodesSync } from '@/routes/admin/utilities/zip-codes';

type ZipCode = {
    id: number;
    zipcode: string;
    place_name: string | null;
};

type Utility = {
    id: number;
    name: string;
    state_id: number;
    state: { id: number; code: string; name: string };
};

type ZipCodeModalProps = {
    utility: Utility | null;
    open: boolean;
    onOpenChange: (open: boolean) => void;
};

export default function ZipCodeModal({ utility, open, onOpenChange }: ZipCodeModalProps) {
    const [zipCodes, setZipCodes] = useState<ZipCode[]>([]);
    const [assignedIds, setAssignedIds] = useState<Set<number>>(new Set());
    const [search, setSearch] = useState('');
    const [loading, setLoading] = useState(false);
    const [saving, setSaving] = useState(false);
    const fileInputRef = useRef<HTMLInputElement>(null);

    useEffect(() => {
        if (!open || !utility) {
            return;
        }

        setLoading(true);
        setSearch('');

        fetch(zipCodesIndex.url(utility.id))
            .then((res) => res.json())
            .then((data: { zip_codes: ZipCode[]; assigned_ids: number[] }) => {
                setZipCodes(data.zip_codes);
                setAssignedIds(new Set(data.assigned_ids));
            })
            .finally(() => setLoading(false));
    }, [open, utility]);

    const filtered = useMemo(() => {
        if (!search) {
            return zipCodes;
        }

        const term = search.toLowerCase();

        return zipCodes.filter(
            (z) =>
                z.zipcode.includes(term) ||
                (z.place_name && z.place_name.toLowerCase().includes(term)),
        );
    }, [zipCodes, search]);

    const toggle = useCallback((id: number) => {
        setAssignedIds((prev) => {
            const next = new Set(prev);
            if (next.has(id)) {
                next.delete(id);
            } else {
                next.add(id);
            }
            return next;
        });
    }, []);

    const selectAllVisible = useCallback(() => {
        setAssignedIds((prev) => {
            const next = new Set(prev);
            for (const z of filtered) {
                next.add(z.id);
            }
            return next;
        });
    }, [filtered]);

    const deselectAllVisible = useCallback(() => {
        setAssignedIds((prev) => {
            const next = new Set(prev);
            const visibleIds = new Set(filtered.map((z) => z.id));
            for (const id of visibleIds) {
                next.delete(id);
            }
            return next;
        });
    }, [filtered]);

    const handleFileUpload = useCallback(
        (e: React.ChangeEvent<HTMLInputElement>) => {
            const file = e.target.files?.[0];

            if (!file) {
                return;
            }

            const reader = new FileReader();

            reader.onload = (event) => {
                const text = event.target?.result as string;
                const uploadedZipCodes = new Set(
                    text
                        .split('\n')
                        .map((line) => line.trim())
                        .filter((line) => line.length > 0),
                );

                const matchedIds = new Set(
                    zipCodes
                        .filter((z) => uploadedZipCodes.has(z.zipcode))
                        .map((z) => z.id),
                );

                setAssignedIds(matchedIds);
            };

            reader.readAsText(file);

            if (fileInputRef.current) {
                fileInputRef.current.value = '';
            }
        },
        [zipCodes],
    );

    const handleSave = useCallback(() => {
        if (!utility) {
            return;
        }

        setSaving(true);

        router.put(
            zipCodesSync.url(utility.id),
            { zip_code_ids: Array.from(assignedIds) },
            {
                onFinish: () => setSaving(false),
                onSuccess: () => onOpenChange(false),
            },
        );
    }, [utility, assignedIds, onOpenChange]);

    const visibleSelectedCount = filtered.filter((z) => assignedIds.has(z.id)).length;

    return (
        <Dialog open={open} onOpenChange={onOpenChange}>
            <DialogContent className="sm:max-w-2xl">
                <DialogHeader>
                    <DialogTitle>Manage zip codes</DialogTitle>
                    <DialogDescription>
                        {utility
                            ? `Select zip codes for ${utility.name} (${utility.state.code})`
                            : 'Loading…'}
                    </DialogDescription>
                </DialogHeader>

                {loading ? (
                    <div className="flex items-center justify-center py-12 text-sm text-zinc-500">
                        Loading zip codes…
                    </div>
                ) : (
                    <div className="space-y-3">
                        <input
                            type="text"
                            value={search}
                            onChange={(e) => setSearch(e.target.value)}
                            placeholder="Search by zip code or city…"
                            className="w-full rounded-md border border-zinc-200 bg-white px-3 py-2 text-sm dark:border-zinc-800 dark:bg-zinc-950"
                        />

                        <div className="flex items-center justify-between text-xs text-zinc-600 dark:text-zinc-400">
                            <span>
                                {assignedIds.size} of {zipCodes.length} selected
                                {search && ` (${visibleSelectedCount} of ${filtered.length} visible)`}
                            </span>
                            <div className="flex gap-3">
                                <button
                                    type="button"
                                    onClick={() => fileInputRef.current?.click()}
                                    className="hover:text-zinc-900 dark:hover:text-zinc-100"
                                >
                                    Upload file
                                </button>
                                <input
                                    ref={fileInputRef}
                                    type="file"
                                    accept=".txt,.csv,text/plain"
                                    className="hidden"
                                    onChange={handleFileUpload}
                                />
                                <button
                                    type="button"
                                    onClick={selectAllVisible}
                                    className="hover:text-zinc-900 dark:hover:text-zinc-100"
                                >
                                    Select all{search ? ' visible' : ''}
                                </button>
                                <button
                                    type="button"
                                    onClick={deselectAllVisible}
                                    className="hover:text-zinc-900 dark:hover:text-zinc-100"
                                >
                                    Deselect all{search ? ' visible' : ''}
                                </button>
                            </div>
                        </div>

                        <div className="max-h-96 overflow-y-auto rounded-md border border-zinc-200 dark:border-zinc-800">
                            {filtered.length === 0 ? (
                                <div className="p-4 text-center text-sm text-zinc-500">
                                    No zip codes found.
                                </div>
                            ) : (
                                filtered.map((z) => (
                                    <label
                                        key={z.id}
                                        className="flex cursor-pointer items-center gap-3 border-b border-zinc-100 px-3 py-2 last:border-b-0 hover:bg-zinc-50 dark:border-zinc-800/50 dark:hover:bg-zinc-900"
                                    >
                                        <Checkbox
                                            checked={assignedIds.has(z.id)}
                                            onCheckedChange={() => toggle(z.id)}
                                        />
                                        <span className="font-mono text-sm">{z.zipcode}</span>
                                        {z.place_name && (
                                            <span className="text-sm text-zinc-500 dark:text-zinc-400">
                                                {z.place_name}
                                            </span>
                                        )}
                                    </label>
                                ))
                            )}
                        </div>

                        <div className="flex items-end justify-end gap-3">
                            <button
                                type="button"
                                onClick={() => onOpenChange(false)}
                                className="rounded-md px-3 py-2 text-sm font-medium text-zinc-600 hover:text-zinc-900 dark:text-zinc-400 dark:hover:text-zinc-100"
                            >
                                Cancel
                            </button>
                            <button
                                type="button"
                                onClick={handleSave}
                                disabled={saving}
                                className="rounded-md bg-zinc-900 px-3 py-2 text-sm font-medium text-white disabled:opacity-60 dark:bg-zinc-100 dark:text-zinc-900"
                            >
                                {saving ? 'Saving…' : 'Save'}
                            </button>
                        </div>
                    </div>
                )}
            </DialogContent>
        </Dialog>
    );
}
