/**
 * @ai-summary Form for searching nearby gas stations
 */

import React, { useState, useEffect, useMemo } from 'react';
import {
  Box,
  TextField,
  Button,
  Slider,
  FormControl,
  FormLabel,
  Alert,
  CircularProgress,
  InputAdornment,
  MenuItem
} from '@mui/material';
import LocationIcon from '@mui/icons-material/LocationOn';
import MyLocationIcon from '@mui/icons-material/MyLocation';
import { StationSearchRequest, GeolocationError } from '../types/stations.types';
import { useGeolocation } from '../hooks';
import { loadGoogleMaps, getGoogleMapsApi } from '../utils/maps-loader';

type Coordinates = {
  latitude: number;
  longitude: number;
};

type GeocoderStatus = 'OK' | 'ZERO_RESULTS' | string;

interface GeocoderResult {
  geometry: {
    location: {
      lat: () => number;
      lng: () => number;
    };
  };
}

interface GoogleMapsGeocoder {
  geocode(
    request: { address: string },
    callback: (results: GeocoderResult[] | null, status: GeocoderStatus) => void
  ): void;
}

type GoogleMapsWithGeocoder = {
  Geocoder: new () => GoogleMapsGeocoder;
};

const US_STATE_OPTIONS = [
  { value: 'AL', label: 'Alabama' },
  { value: 'AK', label: 'Alaska' },
  { value: 'AZ', label: 'Arizona' },
  { value: 'AR', label: 'Arkansas' },
  { value: 'CA', label: 'California' },
  { value: 'CO', label: 'Colorado' },
  { value: 'CT', label: 'Connecticut' },
  { value: 'DE', label: 'Delaware' },
  { value: 'DC', label: 'District of Columbia' },
  { value: 'FL', label: 'Florida' },
  { value: 'GA', label: 'Georgia' },
  { value: 'HI', label: 'Hawaii' },
  { value: 'ID', label: 'Idaho' },
  { value: 'IL', label: 'Illinois' },
  { value: 'IN', label: 'Indiana' },
  { value: 'IA', label: 'Iowa' },
  { value: 'KS', label: 'Kansas' },
  { value: 'KY', label: 'Kentucky' },
  { value: 'LA', label: 'Louisiana' },
  { value: 'ME', label: 'Maine' },
  { value: 'MD', label: 'Maryland' },
  { value: 'MA', label: 'Massachusetts' },
  { value: 'MI', label: 'Michigan' },
  { value: 'MN', label: 'Minnesota' },
  { value: 'MS', label: 'Mississippi' },
  { value: 'MO', label: 'Missouri' },
  { value: 'MT', label: 'Montana' },
  { value: 'NE', label: 'Nebraska' },
  { value: 'NV', label: 'Nevada' },
  { value: 'NH', label: 'New Hampshire' },
  { value: 'NJ', label: 'New Jersey' },
  { value: 'NM', label: 'New Mexico' },
  { value: 'NY', label: 'New York' },
  { value: 'NC', label: 'North Carolina' },
  { value: 'ND', label: 'North Dakota' },
  { value: 'OH', label: 'Ohio' },
  { value: 'OK', label: 'Oklahoma' },
  { value: 'OR', label: 'Oregon' },
  { value: 'PA', label: 'Pennsylvania' },
  { value: 'RI', label: 'Rhode Island' },
  { value: 'SC', label: 'South Carolina' },
  { value: 'SD', label: 'South Dakota' },
  { value: 'TN', label: 'Tennessee' },
  { value: 'TX', label: 'Texas' },
  { value: 'UT', label: 'Utah' },
  { value: 'VT', label: 'Vermont' },
  { value: 'VA', label: 'Virginia' },
  { value: 'WA', label: 'Washington' },
  { value: 'WV', label: 'West Virginia' },
  { value: 'WI', label: 'Wisconsin' },
  { value: 'WY', label: 'Wyoming' }
];

interface StationsSearchFormProps {
  onSearch: (request: StationSearchRequest) => void;
  isSearching?: boolean;
}

/**
 * Search form with manual location input and geolocation button
 * Radius slider: 1-25 miles, default 5 miles
 */
export const StationsSearchForm: React.FC<StationsSearchFormProps> = ({
  onSearch,
  isSearching = false
}) => {
  const [street, setStreet] = useState('');
  const [city, setCity] = useState('');
  const [stateCode, setStateCode] = useState('');
  const [zip, setZip] = useState('');
  const [radius, setRadius] = useState(5); // Miles
  const [locationError, setLocationError] = useState<string | null>(null);
  const [addressError, setAddressError] = useState<string | null>(null);
  const [resolvedCoordinates, setResolvedCoordinates] = useState<Coordinates | null>(null);
  const [isGeocoding, setIsGeocoding] = useState(false);

  const {
    coordinates,
    isPending: isGeolocating,
    error: geoError,
    requestPermission,
    clearError: clearGeoError
  } = useGeolocation();

  // Update form when geolocation succeeds
  useEffect(() => {
    if (coordinates) {
      setResolvedCoordinates({
        latitude: coordinates.latitude,
        longitude: coordinates.longitude
      });
      setLocationError(null);
      setAddressError(null);
    }
  }, [coordinates]);

  // Handle geolocation errors
  useEffect(() => {
    if (geoError) {
      if (geoError === GeolocationError.PERMISSION_DENIED) {
        setLocationError('Location permission denied. Please enable it in browser settings.');
      } else if (geoError === GeolocationError.TIMEOUT) {
        setLocationError('Location request timed out. Try again.');
      } else if (geoError === GeolocationError.POSITION_UNAVAILABLE) {
        setLocationError('Location not available. Try a different device.');
      } else {
        setLocationError('Unable to get location. Please enter your address.');
      }
    }
  }, [geoError]);

  const handleUseCurrentLocation = () => {
    clearGeoError();
    setAddressError(null);
    requestPermission();
  };

  const addressIsComplete = useMemo(
    () => street.trim() !== '' && city.trim() !== '' && stateCode !== '' && zip.trim().length === 5,
    [street, city, stateCode, zip]
  );

  const markManualAddressInput = () => {
    setResolvedCoordinates(null);
    setAddressError(null);
    setLocationError(null);
  };

  const handleSearch = async (): Promise<void> => {
    const submitWithCoordinates = (coords: Coordinates) => {
      const request: StationSearchRequest = {
        latitude: coords.latitude,
        longitude: coords.longitude,
        radius: radius * 1609.34 // Convert miles to meters
      };

      onSearch(request);
    };

    if (resolvedCoordinates) {
      submitWithCoordinates(resolvedCoordinates);
      return;
    }

    if (!addressIsComplete) {
      setAddressError('Enter Street, City, State, and ZIP or use current location.');
      return;
    }

    try {
      setIsGeocoding(true);
      await loadGoogleMaps();
      const maps = getGoogleMapsApi() as unknown as GoogleMapsWithGeocoder;
      const geocoder = new maps.Geocoder();
      const formattedAddress = [street, city, stateCode, zip].filter(Boolean).join(', ');

      const coords = await new Promise<Coordinates>((resolve, reject) => {
        geocoder.geocode({ address: formattedAddress }, (results, status) => {
          if (status === 'OK' && results && results[0]) {
            const location = results[0].geometry.location;
            resolve({
              latitude: location.lat(),
              longitude: location.lng()
            });
            return;
          }

          if (status === 'ZERO_RESULTS') {
            reject(new Error('Address not found. Please double-check the details.'));
            return;
          }

          reject(new Error('Unable to locate that address right now. Try again shortly.'));
        });
      });

      setResolvedCoordinates(coords);
      setLocationError(null);
      setAddressError(null);
      submitWithCoordinates(coords);
    } catch (error) {
      setAddressError(error instanceof Error ? error.message : 'Unable to locate that address.');
    } finally {
      setIsGeocoding(false);
    }
  };

  const handleRadiusChange = (
    _event: Event,
    newValue: number | number[]
  ) => {
    if (typeof newValue === 'number') {
      setRadius(newValue);
    }
  };

  return (
    <Box
      component="form"
      onSubmit={(e) => {
        e.preventDefault();
        void handleSearch();
      }}
      sx={{ padding: 2, display: 'flex', flexDirection: 'column', gap: 2 }}
    >
      {/* Geolocation Button */}
      <Button
        variant="contained"
        startIcon={isGeolocating ? <CircularProgress size={20} /> : <MyLocationIcon />}
        onClick={handleUseCurrentLocation}
        disabled={isGeolocating}
        fullWidth
      >
        {isGeolocating ? 'Getting location...' : 'Use Current Location'}
      </Button>

      {/* Or Divider */}
      <Box sx={{ textAlign: 'center', color: 'textSecondary' }}>or</Box>

      {/* Street Address Input */}
      <TextField
        label="Street"
        name="street"
        value={street}
        onChange={(e) => {
          setStreet(e.target.value);
          markManualAddressInput();
        }}
        placeholder="123 Main St"
        autoComplete="address-line1"
        fullWidth
        InputProps={{
          startAdornment: (
            <InputAdornment position="start">
              <LocationIcon />
            </InputAdornment>
          )
        }}
      />

      {/* City Input */}
      <TextField
        label="City"
        name="city"
        value={city}
        onChange={(e) => {
          setCity(e.target.value);
          markManualAddressInput();
        }}
        placeholder="Madison"
        autoComplete="address-level2"
        fullWidth
      />

      {/* State and ZIP */}
      <Box
        sx={{
          display: 'grid',
          gridTemplateColumns: { xs: '1fr', sm: '1fr 1fr' },
          gap: 2,
          width: '100%'
        }}
      >
        <TextField
          select
          label="State"
          value={stateCode}
          onChange={(e) => {
            setStateCode(e.target.value);
            markManualAddressInput();
          }}
          SelectProps={{
            displayEmpty: true,
            renderValue: (selected) => {
              const value = selected as string;
              if (!value) {
                return 'Select state';
              }
              const option = US_STATE_OPTIONS.find((state) => state.value === value);
              return option ? option.label : value;
            }
          }}
          InputLabelProps={{ shrink: true }}
          inputProps={{ name: 'state' }}
          fullWidth
        >
          <MenuItem value="">
            <em>Select state</em>
          </MenuItem>
          {US_STATE_OPTIONS.map((state) => (
            <MenuItem key={state.value} value={state.value}>
              {state.label}
            </MenuItem>
          ))}
        </TextField>

        <TextField
          label="ZIP"
          name="zip"
          value={zip}
          onChange={(e) => {
            const sanitized = e.target.value.replace(/[^0-9]/g, '').slice(0, 5);
            setZip(sanitized);
            markManualAddressInput();
          }}
          placeholder="94105"
          inputProps={{
            inputMode: 'numeric',
            pattern: '[0-9]*',
            maxLength: 5
          }}
          autoComplete="postal-code"
          fullWidth
        />
      </Box>

      {/* Radius Slider */}
      <FormControl fullWidth>
        <FormLabel>Search Radius: {radius} mi</FormLabel>
        <Slider
          data-testid="radius-slider"
          value={radius}
          onChange={handleRadiusChange}
          min={1}
          max={25}
          step={0.5}
          marks={[
            { value: 1, label: '1 mi' },
            { value: 5, label: '5 mi' },
            { value: 10, label: '10 mi' },
            { value: 25, label: '25 mi' }
          ]}
          sx={{ marginTop: 2, marginBottom: 1 }}
        />
      </FormControl>

      {/* Error Messages */}
      {(locationError || addressError) && (
        <Alert severity="error">{locationError || addressError}</Alert>
      )}

      {/* Search Button */}
      <Button
        variant="contained"
        color="primary"
        onClick={() => {
          void handleSearch();
        }}
        disabled={
          isSearching ||
          isGeocoding ||
          (!resolvedCoordinates && !addressIsComplete)
        }
        sx={{
          minHeight: '44px',
          marginTop: 1
        }}
      >
        {isSearching || isGeocoding ? (
          <CircularProgress size={24} />
        ) : (
          'Search Stations'
        )}
      </Button>
    </Box>
  );
};

export default StationsSearchForm;
