'use client';

import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
import { GoogleMap, Marker, useLoadScript } from '@react-google-maps/api';
import { MapPin, Navigation, Loader2, Search, X } from 'lucide-react';
import {
  AREA_COORDINATES,
  AREA_LABELS,
  DAVAO_REGION_CENTER,
  SERVICE_AREA_OPTIONS,
  WORKER_SERVICE_AREA_OPTIONS,
  findNearestServiceArea,
  type ServiceAreaKey,
} from '@/lib/geocode';
import {
  CITY_DEFINITIONS,
  ISLAND_GROUP_IDS,
  ISLAND_GROUP_LABELS,
  WORKER_SERVICE_AREA_GROUPS,
  type IslandGroupId,
} from '@ehandyhire/shared';
import { GOOGLE_MAPS_SCRIPT_ID } from '@/components/map/LocationMap';
import { Button } from '@/components/ui/button';
import { isWithinPhilippines } from '@/lib/regionGeolocation';
import { cn } from '@/lib/utils';

export interface JobLocationValue {
  city: ServiceAreaKey;
  address: string;
  location: string;
  latitude: number;
  longitude: number;
}

export function buildJobLocation(
  city: ServiceAreaKey,
  address: string,
  coords?: { lat: number; lng: number }
): JobLocationValue {
  const cityLabel = AREA_LABELS[city] || city;
  const location = address.trim() ? `${address.trim()}, ${cityLabel}` : cityLabel;
  const fallback = AREA_COORDINATES[city] || DAVAO_REGION_CENTER;
  const lat = coords?.lat ?? fallback.lat;
  const lng = coords?.lng ?? fallback.lng;
  return {
    city,
    address: address.trim(),
    location,
    latitude: lat,
    longitude: lng,
  };
}

function useCurrentLocation() {
  const [loading, setLoading] = useState(false);
  const [error, setError] = useState<string | null>(null);

  const getPosition = useCallback((): Promise<GeolocationPosition> => {
    return new Promise((resolve, reject) => {
      if (!navigator.geolocation) {
        reject(new Error('Geolocation is not supported by your browser'));
        return;
      }
      navigator.geolocation.getCurrentPosition(resolve, reject, {
        enableHighAccuracy: true,
        timeout: 15000,
        maximumAge: 60000,
      });
    });
  }, []);

  const locate = useCallback(async () => {
    setLoading(true);
    setError(null);
    try {
      const position = await getPosition();
      return {
        lat: position.coords.latitude,
        lng: position.coords.longitude,
      };
    } catch (err) {
      const message =
        err instanceof GeolocationPositionError
          ? err.code === err.PERMISSION_DENIED
            ? 'Location permission denied. Allow access in browser settings.'
            : err.code === err.TIMEOUT
              ? 'Location request timed out. Try again.'
              : 'Could not get your location.'
          : err instanceof Error
            ? err.message
            : 'Could not get your location.';
      setError(message);
      return null;
    } finally {
      setLoading(false);
    }
  }, [getPosition]);

  return { locate, loading, error, clearError: () => setError(null) };
}

interface LocationPickerProps {
  value: JobLocationValue;
  onChange: (value: JobLocationValue) => void;
}

export function LocationPicker({ value, onChange }: LocationPickerProps) {
  const apiKey = process.env.NEXT_PUBLIC_GOOGLE_MAPS_API_KEY ?? '';
  const { isLoaded } = useLoadScript({
    googleMapsApiKey: apiKey,
    id: GOOGLE_MAPS_SCRIPT_ID,
    preventGoogleFontsLoading: true,
  });

  const [pin, setPin] = useState({ lat: value.latitude, lng: value.longitude });
  const mapRef = useRef<google.maps.Map | null>(null);
  const { locate, loading: locating, error: locateError } = useCurrentLocation();

  useEffect(() => {
    setPin({ lat: value.latitude, lng: value.longitude });
  }, [value.latitude, value.longitude]);

  const applyCoordinates = useCallback(
    async (lat: number, lng: number) => {
      const city = findNearestServiceArea(lat, lng);
      let address = value.address;

      if (isLoaded && window.google?.maps?.Geocoder) {
        try {
          const formatted = await new Promise<string | undefined>((resolve) => {
            const geocoder = new google.maps.Geocoder();
            geocoder.geocode({ location: { lat, lng } }, (results, status) => {
              if (status === 'OK' && results?.[0]) {
                resolve(results[0].formatted_address);
              } else {
                resolve(undefined);
              }
            });
          });
          if (formatted) {
            const cityLabel = AREA_LABELS[city];
            address = formatted.replace(new RegExp(`,?\\s*${cityLabel}.*$`, 'i'), '').trim() || formatted;
          }
        } catch {
          // keep existing address if reverse geocode fails
        }
      }

      const next = buildJobLocation(city, address, { lat, lng });
      onChange(next);
      setPin({ lat, lng });
      mapRef.current?.panTo({ lat, lng });
      mapRef.current?.setZoom(15);
    },
    [isLoaded, onChange, value.address]
  );

  const handleUseCurrentLocation = async () => {
    const coords = await locate();
    if (coords) await applyCoordinates(coords.lat, coords.lng);
  };

  const updateCity = (city: ServiceAreaKey) => {
    const next = buildJobLocation(city, value.address);
    onChange(next);
    setPin({ lat: next.latitude, lng: next.longitude });
  };

  const updateAddress = (address: string) => {
    onChange(buildJobLocation(value.city, address, { lat: value.latitude, lng: value.longitude }));
  };

  const handleMapClick = useCallback(
    (e: google.maps.MapMouseEvent) => {
      if (!e.latLng) return;
      const lat = e.latLng.lat();
      const lng = e.latLng.lng();
      setPin({ lat, lng });
      onChange({ ...value, latitude: lat, longitude: lng });
    },
    [onChange, value]
  );

  return (
    <div className="space-y-3">
      <div>
        <label className="text-sm font-medium text-slate-700">City / Area</label>
        <select
          value={value.city}
          onChange={(e) => updateCity(e.target.value as ServiceAreaKey)}
          className="form-select"
          required
        >
          {SERVICE_AREA_OPTIONS.length > 20 ? (
            WORKER_SERVICE_AREA_GROUPS.map((group) => (
              <optgroup key={group.id} label={group.label}>
                {group.options.map((opt) => (
                  <option key={opt.area} value={opt.area}>
                    {opt.label}
                  </option>
                ))}
              </optgroup>
            ))
          ) : (
            SERVICE_AREA_OPTIONS.map((opt) => (
              <option key={opt.value} value={opt.value}>
                {opt.label}
              </option>
            ))
          )}
        </select>
      </div>

      <div>
        <label className="text-sm font-medium text-slate-700">Street / Barangay (optional)</label>
        <input
          type="text"
          value={value.address}
          onChange={(e) => updateAddress(e.target.value)}
          placeholder="e.g. Buhangin, Matina"
          className="search-input mt-1.5"
        />
      </div>

      <div>
        <div className="flex flex-col gap-2 sm:flex-row sm:items-center sm:justify-between sm:gap-3 mb-1.5">
          <label className="text-sm font-medium text-slate-700 flex items-center gap-1.5">
            <MapPin className="h-4 w-4 text-primary shrink-0" />
            Pin on map
          </label>
          <Button
            type="button"
            variant="outline"
            size="sm"
            className="h-9 w-full sm:w-auto text-xs gap-1.5 shrink-0"
            onClick={handleUseCurrentLocation}
            disabled={locating}
          >
            {locating ? (
              <Loader2 className="h-3.5 w-3.5 animate-spin" />
            ) : (
              <Navigation className="h-3.5 w-3.5" />
            )}
            Use my location
          </Button>
        </div>
        <p className="text-xs text-muted mb-2">Click the map or use your GPS to set the exact location.</p>
        {locateError && (
          <p className="text-xs text-red-600 mb-2">{locateError}</p>
        )}
        {!apiKey ? (
          <div className="h-40 sm:h-44 md:h-52 lg:h-56 rounded-lg border border-border bg-slate-50 flex items-center justify-center text-sm text-muted px-4 text-center">
            Map preview unavailable
          </div>
        ) : !isLoaded ? (
          <div className="h-40 sm:h-44 md:h-52 lg:h-56 rounded-lg border border-border bg-slate-50 flex items-center justify-center">
            <div className="loading-spinner" />
          </div>
        ) : (
          <div className="h-40 sm:h-44 md:h-52 lg:h-56 rounded-lg border border-border overflow-hidden">
            <GoogleMap
              mapContainerStyle={{ width: '100%', height: '100%' }}
              center={pin}
              zoom={13}
              onClick={handleMapClick}
              onLoad={(map) => { mapRef.current = map; }}
              options={{
                disableDefaultUI: true,
                zoomControl: true,
                streetViewControl: false,
                mapTypeControl: false,
              }}
            >
              <Marker position={pin} />
            </GoogleMap>
          </div>
        )}
      </div>
    </div>
  );
}

interface ServiceAreaPickerProps {
  value: ServiceAreaKey[];
  onChange: (areas: ServiceAreaKey[]) => void;
}

const ADMIN_REGION_ISLAND = new Map<string, IslandGroupId>();
for (const city of CITY_DEFINITIONS) {
  if (!ADMIN_REGION_ISLAND.has(city.adminRegion)) {
    ADMIN_REGION_ISLAND.set(city.adminRegion, city.islandGroup);
  }
}

type IslandFilter = 'ALL' | IslandGroupId;

const ISLAND_FILTERS: { id: IslandFilter; label: string }[] = [
  { id: 'ALL', label: 'All' },
  ...ISLAND_GROUP_IDS.map((id) => ({
    id,
    label: ISLAND_GROUP_LABELS[id],
  })),
];

export function ServiceAreaPicker({ value, onChange }: ServiceAreaPickerProps) {
  const { locate, loading: locating, error: locateError } = useCurrentLocation();
  const [search, setSearch] = useState('');
  const [islandFilter, setIslandFilter] = useState<IslandFilter>('ALL');
  const [outsidePhError, setOutsidePhError] = useState<string | null>(null);

  const query = search.trim().toLowerCase();

  const filteredGroups = useMemo(() => {
    return WORKER_SERVICE_AREA_GROUPS.filter((group) => {
      if (islandFilter !== 'ALL' && ADMIN_REGION_ISLAND.get(group.id) !== islandFilter) {
        return false;
      }
      if (!query) return true;
      if (group.label.toLowerCase().includes(query)) return true;
      return group.options.some(
        (opt) =>
          opt.label.toLowerCase().includes(query) ||
          opt.slug.replace(/-/g, ' ').includes(query)
      );
    }).map((group) => ({
      ...group,
      options: query
        ? group.options.filter(
            (opt) =>
              group.label.toLowerCase().includes(query) ||
              opt.label.toLowerCase().includes(query) ||
              opt.slug.replace(/-/g, ' ').includes(query)
          )
        : group.options,
    })).filter((group) => group.options.length > 0);
  }, [islandFilter, query]);

  const handleUseCurrentLocation = async () => {
    setOutsidePhError(null);
    const coords = await locate();
    if (!coords) return;
    if (!isWithinPhilippines(coords.lat, coords.lng)) {
      setOutsidePhError('Your location appears to be outside the Philippines.');
      return;
    }
    const nearest = findNearestServiceArea(coords.lat, coords.lng);
    if (!value.includes(nearest)) {
      onChange([...value, nearest]);
    }
  };

  const toggle = (area: ServiceAreaKey) => {
    if (value.includes(area)) {
      if (value.length === 1) return;
      onChange(value.filter((a) => a !== area));
    } else {
      onChange([...value, area]);
    }
  };

  const removeArea = (area: ServiceAreaKey) => {
    if (value.length === 1) return;
    onChange(value.filter((a) => a !== area));
  };

  return (
    <div className="space-y-3">
      <div>
        <label className="text-sm font-medium text-slate-700 flex items-center gap-1.5">
          <MapPin className="h-4 w-4 text-primary" />
          Service areas
        </label>
        <p className="mt-1 text-xs text-muted">
          Select cities where you work — customers will find you in these areas nationwide.
        </p>
      </div>

      {value.length > 0 && (
        <div className="flex flex-wrap gap-2">
          {value.map((area) => (
            <span
              key={area}
              className="inline-flex items-center gap-1 rounded-full bg-primary/10 px-2.5 py-1 text-xs font-semibold text-primary-dark"
            >
              {AREA_LABELS[area] || area}
              {value.length > 1 && (
                <button
                  type="button"
                  onClick={() => removeArea(area)}
                  className="rounded-full p-0.5 hover:bg-primary/20"
                  aria-label={`Remove ${AREA_LABELS[area] || area}`}
                >
                  <X className="h-3 w-3" />
                </button>
              )}
            </span>
          ))}
        </div>
      )}

      <Button
        type="button"
        variant="outline"
        size="sm"
        className="h-9 w-full sm:w-auto text-xs gap-1.5"
        onClick={handleUseCurrentLocation}
        disabled={locating}
      >
        {locating ? (
          <Loader2 className="h-3.5 w-3.5 animate-spin" />
        ) : (
          <Navigation className="h-3.5 w-3.5" />
        )}
        Add my current city
      </Button>
      {locateError && <p className="text-xs text-red-600">{locateError}</p>}
      {outsidePhError && <p className="text-xs text-red-600">{outsidePhError}</p>}

      <div className="relative">
        <Search className="pointer-events-none absolute left-3 top-1/2 h-4 w-4 -translate-y-1/2 text-slate-400" />
        <input
          type="search"
          value={search}
          onChange={(e) => setSearch(e.target.value)}
          placeholder="Search city or region…"
          autoComplete="off"
          className="search-input-icon h-10 w-full text-sm"
        />
      </div>

      {!query && (
        <div className="-mx-1 flex gap-2 overflow-x-auto px-1 pb-1 [scrollbar-width:none] [&::-webkit-scrollbar]:hidden">
          {ISLAND_FILTERS.map((filter) => {
            const active = islandFilter === filter.id;
            return (
              <button
                key={filter.id}
                type="button"
                onClick={() => setIslandFilter(filter.id)}
                className={cn(
                  'shrink-0 rounded-full px-3.5 py-2 text-xs font-semibold transition-colors',
                  active
                    ? 'bg-slate-800 text-white'
                    : 'bg-slate-100 text-slate-600 hover:bg-slate-200'
                )}
              >
                {filter.label}
              </button>
            );
          })}
        </div>
      )}

      <div className="max-h-[min(50vh,28rem)] space-y-4 overflow-y-auto overscroll-contain pr-1">
        {filteredGroups.length === 0 ? (
          <p className="py-6 text-center text-sm text-muted">
            No cities match &ldquo;{search.trim()}&rdquo;
          </p>
        ) : (
          filteredGroups.map((group) => (
            <div key={group.id}>
              <p className="mb-1.5 text-xs font-semibold text-slate-500">{group.label}</p>
              <div className="flex flex-wrap gap-2">
                {group.options.map((opt) => {
                  const area = opt.area as ServiceAreaKey;
                  const selected = value.includes(area);
                  return (
                    <button
                      key={opt.area}
                      type="button"
                      onClick={() => toggle(area)}
                      className={cn('filter-chip min-h-9', selected && 'filter-chip-active')}
                    >
                      {opt.label}
                    </button>
                  );
                })}
              </div>
            </div>
          ))
        )}
      </div>
    </div>
  );
}
