import React, { useEffect, useMemo, useState } from 'react';

import { Autocomplete, TextField, CircularProgress } from '@mui/material';
import { debounce } from '@mui/material/utils';

// Free OSM Nominatim geocoder — replaces Google Places Autocomplete /
// react-geocode across the console. No API key required. Nominatim's usage
// policy caps the public endpoint at ~1 req/sec, so every caller here is
// debounced (500ms) and cancels stale in-flight predictions.
const NOMINATIM_SEARCH_URL = 'https://nominatim.openstreetmap.org/search';

// Builds a Google-Places-shaped `address_components` array (each entry
// `{ long_name, short_name, types: [] }`) from Nominatim's flat `address`
// object, so existing call sites' `place.address_components.forEach(...)`
// type-switch parsing keeps working unchanged.
const buildAddressComponents = (addr = {}) => {
  const components = [];
  const push = (longName, types) => {
    if (longName) components.push({ long_name: longName, short_name: longName, types });
  };
  push(addr.house_number, ['street_number']);
  push(addr.road || addr.pedestrian, ['route']);
  push(addr.suburb || addr.neighbourhood || addr.quarter, ['sublocality_level_1', 'sublocality']);
  push(addr.city_district || addr.county, ['administrative_area_level_3']);
  push(addr.city || addr.town || addr.village, ['locality']);
  push(addr.state, ['administrative_area_level_1']);
  push(addr.country, ['country']);
  push(addr.postcode, ['postal_code']);
  return components;
};

// Shapes a Nominatim result close enough to a Google Places `place` object
// (formatted_address, geometry.location.lat()/lng(), address_components)
// that call sites need only mechanical edits, not rewrites.
export const toPlace = (result) => ({
  formatted_address: result.display_name,
  name: result.display_name?.split(',')[0] || '',
  geometry: {
    location: {
      lat: () => parseFloat(result.lat),
      lng: () => parseFloat(result.lon)
    }
  },
  address_components: buildAddressComponents(result.address)
});

const buildParams = (query, bias) => {
  const params = new URLSearchParams({ q: query, format: 'json', addressdetails: '1', limit: '5' });
  if (bias?.lat && bias?.lng) {
    const d = 0.5; // ~55km box around the operator's zone — soft bias, not a hard filter
    params.set('viewbox', `${bias.lng - d},${bias.lat + d},${bias.lng + d},${bias.lat - d}`);
  }
  return params;
};

// Standalone forward-geocode helper — replaces `Geocode.fromAddress(address)`
// for call sites that resolve a plain text address without a predictions
// dropdown (e.g. re-geocoding on submit). Returns a `toPlace`-shaped object,
// or null if nothing matched.
export async function geocodeAddress(address, { bias } = {}) {
  if (!address) return null;
  try {
    const params = buildParams(address, bias);
    params.set('limit', '1');
    const res = await fetch(`${NOMINATIM_SEARCH_URL}?${params.toString()}`, { headers: { Accept: 'application/json' } });
    if (!res.ok) return null;
    const results = await res.json();
    if (!results?.length) return null;
    return toPlace(results[0]);
  } catch (err) {
    console.error('geocodeAddress error:', err);
    return null;
  }
}

// Address search-as-you-type — replaces Google Places Autocomplete
// (usePlacesWidget / window.google.maps.places.Autocomplete /
// AutocompleteService) across the console. Renders as an MUI Autocomplete
// with freeSolo so the operator can still submit free text.
//
// Props:
//   value / onChange(text)        — controlled free-text input value
//   onPlaceSelected(place)        — fired when a suggestion is chosen; `place`
//                                    is shaped like a Google Places `place`
//   bias { lat, lng }             — optional soft bias toward the operator's zone
const AddressAutocomplete = ({
  id,
  label,
  placeholder = 'Search address',
  value,
  onChange,
  onPlaceSelected,
  bias,
  sx,
  textFieldSx,
  fullWidth = true,
  disabled,
  inputRef,
  TextFieldProps = {}
}) => {
  const [inputValue, setInputValue] = useState(value || '');
  const [options, setOptions] = useState([]);
  const [loading, setLoading] = useState(false);

  useEffect(() => {
    if (value !== undefined && value !== inputValue) setInputValue(value || '');
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, [value]);

  const fetchPredictions = useMemo(
    () =>
      debounce((query, bias1, callback) => {
        if (!query) {
          callback([]);
          return;
        }
        const params = buildParams(query, bias1);
        fetch(`${NOMINATIM_SEARCH_URL}?${params.toString()}`, { headers: { Accept: 'application/json' } })
          .then((res) => (res.ok ? res.json() : []))
          .then((results) => callback(results || []))
          .catch(() => callback([]));
      }, 500),
    []
  );

  useEffect(() => {
    let active = true;
    if (!inputValue) {
      setOptions([]);
      setLoading(false);
      return undefined;
    }
    setLoading(true);
    fetchPredictions(inputValue, bias, (results) => {
      if (!active) return;
      setLoading(false);
      setOptions(results);
    });
    return () => {
      active = false;
    };
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, [inputValue, bias?.lat, bias?.lng, fetchPredictions]);

  return (
    <Autocomplete
      id={id}
      freeSolo
      fullWidth={fullWidth}
      disabled={disabled}
      sx={sx}
      options={options}
      filterOptions={(x) => x}
      getOptionLabel={(option) => (typeof option === 'string' ? option : option.display_name || '')}
      isOptionEqualToValue={(option, val) => option.place_id === val?.place_id}
      inputValue={inputValue}
      onInputChange={(event, newInputValue) => {
        setInputValue(newInputValue);
        onChange?.(newInputValue);
      }}
      onChange={(event, selected) => {
        if (selected && typeof selected !== 'string') {
          setInputValue(selected.display_name || '');
          onPlaceSelected?.(toPlace(selected));
        }
      }}
      renderInput={(params) => (
        <TextField
          {...params}
          {...TextFieldProps}
          inputRef={inputRef}
          label={label}
          placeholder={placeholder}
          sx={textFieldSx}
          autoComplete="off"
          InputProps={{
            ...params.InputProps,
            ...TextFieldProps.InputProps,
            startAdornment: TextFieldProps.InputProps?.startAdornment ?? params.InputProps.startAdornment,
            endAdornment: (
              <>
                {loading ? <CircularProgress color="inherit" size={16} /> : null}
                {TextFieldProps.InputProps?.endAdornment ?? params.InputProps.endAdornment}
              </>
            )
          }}
        />
      )}
    />
  );
};

export default AddressAutocomplete;
