/**
 * @license
 * SPDX-License-Identifier: Apache-2.0
 */

/**
 * Address autocomplete — a keyless replacement for the merchant_web Google Places
 * field. It queries OpenStreetMap Nominatim (same keyless provider family the
 * Dispatch map's OSRM routing uses) and parses the picked place into the discrete
 * address fields the user-create form needs: address, suburb, city, state,
 * postcode (+ lat/long). No API key required.
 *
 * Nominatim usage policy: light, debounced, one request per keystroke-pause.
 */

import React, { useEffect, useRef, useState } from 'react';
import { MapPin, Loader2, Search } from 'lucide-react';

export interface AddressResult {
  address: string;
  suburb: string;
  city: string;
  state: string;
  postcode: string;
  latitude: string;
  longitude: string;
}

interface NominatimRow {
  display_name?: string;
  lat?: string;
  lon?: string;
  address?: Record<string, string>;
}

/** Map a Nominatim `address` object onto our discrete fields (most-specific first). */
function parseRow(row: NominatimRow): AddressResult {
  const a = row.address ?? {};
  return {
    address: row.display_name ?? '',
    suburb: a.suburb || a.neighbourhood || a.city_district || a.hamlet || a.quarter || '',
    city: a.city || a.town || a.village || a.municipality || a.county || a.state_district || '',
    state: a.state || '',
    postcode: a.postcode || '',
    latitude: row.lat ?? '',
    longitude: row.lon ?? '',
  };
}

export default function AddressAutocomplete({
  value = '',
  placeholder = 'Search address…',
  onSelect,
}: {
  value?: string;
  placeholder?: string;
  onSelect: (result: AddressResult | null) => void;
}) {
  const [query, setQuery] = useState(value);
  const [options, setOptions] = useState<NominatimRow[]>([]);
  const [open, setOpen] = useState(false);
  const [loading, setLoading] = useState(false);
  const [highlight, setHighlight] = useState(-1);
  const boxRef = useRef<HTMLDivElement>(null);

  // Keep the input in sync if the parent resets the value (e.g. after submit).
  useEffect(() => { setQuery(value); }, [value]);

  // Debounced Nominatim lookup.
  useEffect(() => {
    const q = query.trim();
    if (q.length < 3) { setOptions([]); setOpen(false); return; }
    let active = true;
    setLoading(true);
    const t = setTimeout(async () => {
      try {
        const res = await fetch(
          `https://nominatim.openstreetmap.org/search?format=json&addressdetails=1&limit=6&q=${encodeURIComponent(q)}`,
          { headers: { Accept: 'application/json' } },
        );
        const data = (await res.json()) as NominatimRow[];
        if (active) { setOptions(Array.isArray(data) ? data : []); setOpen(true); setHighlight(-1); }
      } catch {
        if (active) setOptions([]);
      } finally {
        if (active) setLoading(false);
      }
    }, 450);
    return () => { active = false; clearTimeout(t); };
  }, [query]);

  // Close on outside click.
  useEffect(() => {
    const onDoc = (e: MouseEvent) => {
      if (boxRef.current && !boxRef.current.contains(e.target as Node)) setOpen(false);
    };
    document.addEventListener('mousedown', onDoc);
    return () => document.removeEventListener('mousedown', onDoc);
  }, []);

  const pick = (row: NominatimRow) => {
    const result = parseRow(row);
    setQuery(result.address);
    setOptions([]);
    setOpen(false);
    onSelect(result);
  };

  const onKeyDown = (e: React.KeyboardEvent) => {
    if (!open || options.length === 0) return;
    if (e.key === 'ArrowDown') { e.preventDefault(); setHighlight((h) => Math.min(h + 1, options.length - 1)); }
    else if (e.key === 'ArrowUp') { e.preventDefault(); setHighlight((h) => Math.max(h - 1, 0)); }
    else if (e.key === 'Enter' && highlight >= 0) { e.preventDefault(); pick(options[highlight]); }
    else if (e.key === 'Escape') { setOpen(false); }
  };

  return (
    <div className="relative" ref={boxRef}>
      <span className="absolute inset-y-0 left-0 pl-3.5 flex items-center pointer-events-none text-slate-400">
        <Search size={14} />
      </span>
      <input
        type="text"
        value={query}
        placeholder={placeholder}
        autoComplete="off"
        onChange={(e) => { setQuery(e.target.value); if (!e.target.value) onSelect(null); }}
        onFocus={() => { if (options.length) setOpen(true); }}
        onKeyDown={onKeyDown}
        className="w-full border border-slate-200 rounded-xl pl-10 pr-9 py-3 bg-slate-50/40 hover:bg-slate-100 focus:bg-white outline-none focus:border-purple-500 focus:ring-4 focus:ring-purple-500/10 transition-all text-slate-800 font-semibold text-sm shadow-sm"
      />
      {loading && <span className="absolute inset-y-0 right-0 pr-3 flex items-center text-slate-400"><Loader2 size={14} className="animate-spin" /></span>}

      {open && options.length > 0 && (
        <ul className="absolute z-20 mt-1 w-full bg-white border border-slate-200 rounded-xl shadow-lg max-h-60 overflow-y-auto py-1">
          {options.map((o, i) => (
            <li key={`${o.lat},${o.lon},${i}`}>
              <button
                type="button"
                onMouseEnter={() => setHighlight(i)}
                onClick={() => pick(o)}
                className={`w-full text-left px-3 py-2 flex items-start gap-2 cursor-pointer transition-colors ${
                  highlight === i ? 'bg-purple-50' : 'hover:bg-slate-50'
                }`}
              >
                <MapPin size={13} className="text-purple-500 shrink-0 mt-0.5" />
                <span className="text-[12.5px] text-slate-700 leading-snug">{o.display_name}</span>
              </button>
            </li>
          ))}
        </ul>
      )}
    </div>
  );
}
