"use client";

import { useEffect, useRef, useState } from "react";
import { ENV } from "../../core/env";
import { Input } from "../../shadcnui";
import { cn } from "../../utils";
import { FormFieldWrapper } from "./FormFieldWrapper";

/**
 * FormPlaceAutocomplete component integrates Google Places API (New)
 * to provide address suggestions as the user types.
 *
 * Prerequisites:
 * 1. Set NEXT_PUBLIC_GOOGLE_MAPS_API_KEY environment variable
 * 2. Enable Places API (New) in Google Cloud Console
 * 3. Configure API key restrictions as needed
 *
 * Note: This uses the new Places API via REST calls, not the legacy JavaScript API
 */

export interface PlaceAddressComponents {
  street_number: string;
  street: string;
  city: string;
  province: string;
  region: string;
  postcode: string;
  country: string;
  country_code: string;
}

export interface PlaceSuggestion {
  place_id: string;
  description: string;
  structured_formatting: {
    main_text: string;
    secondary_text: string;
  };
  addressComponents?: PlaceAddressComponents;
}

interface PlaceAutocompleteProps {
  form: any;
  id: string;
  name?: string;
  description?: string;
  placeholder?: string;
  disabled?: boolean;
  testId?: string;
  isRequired?: boolean;
  onPlaceSelect?: (place: PlaceSuggestion) => void;
  className?: string;
  /**
   * Optional array of place types to include in search results.
   * When not specified, no type restriction is applied (all place types returned).
   * For addresses, use: ["street_address", "premise", "subpremise"]
   * For cities, use: ["locality", "administrative_area_level_3"]
   * For regions, use: ["administrative_area_level_1", "administrative_area_level_2"]
   */
  includeTypes?: string[];
  /** Google Places language code for results. Defaults to "en". */
  languageCode?: string;
}

export function FormPlaceAutocomplete({
  form,
  id,
  name,
  description,
  placeholder,
  disabled,
  testId,
  isRequired = false,
  onPlaceSelect,
  className,
  includeTypes,
  languageCode = "en",
}: PlaceAutocompleteProps) {
  const [inputValue, setInputValue] = useState("");
  const [suggestions, setSuggestions] = useState<PlaceSuggestion[]>([]);
  const [isLoading, setIsLoading] = useState(false);
  const [showSuggestions, setShowSuggestions] = useState(false);
  const [loadError, setLoadError] = useState(false);
  const [apiKey, setApiKey] = useState<string | null>(null);
  const debounceRef = useRef<NodeJS.Timeout | null>(null);
  const containerRef = useRef<HTMLDivElement>(null);

  // Initialize API key
  useEffect(() => {
    const key = ENV.GOOGLE_MAPS_API_KEY;

    if (!key) {
      console.error("Google Maps API key not found. Please set NEXT_PUBLIC_GOOGLE_MAPS_API_KEY environment variable.");
      setLoadError(true);
      return;
    }

    setApiKey(key);
  }, []);

  // Update input value when form value changes
  useEffect(() => {
    const formValue = form.getValues(id);
    if (formValue !== inputValue) {
      setInputValue(formValue || "");
    }
  }, [form.watch(id), id, inputValue]);

  // Fetch place suggestions from Google Places API (New)
  const fetchSuggestions = async (input: string) => {
    if (!apiKey) return;

    try {
      setIsLoading(true);

      // Using the new Places API autocomplete endpoint
      const response = await fetch(`https://places.googleapis.com/v1/places:autocomplete`, {
        method: "POST",
        headers: {
          "Content-Type": "application/json",
          "X-Goog-Api-Key": apiKey,
        },
        body: JSON.stringify({
          input: input,
          ...(includeTypes ? { includedPrimaryTypes: includeTypes } : {}),
          languageCode: languageCode,
        }),
      });

      if (!response.ok) {
        throw new Error(`Places API error: ${response.status}`);
      }

      const data = await response.json();

      if (data.suggestions) {
        const formattedSuggestions: PlaceSuggestion[] = data.suggestions.map((suggestion: any) => ({
          place_id: suggestion.placePrediction?.placeId || "",
          description: suggestion.placePrediction?.text?.text || "",
          structured_formatting: {
            main_text: suggestion.placePrediction?.structuredFormat?.mainText?.text || "",
            secondary_text: suggestion.placePrediction?.structuredFormat?.secondaryText?.text || "",
          },
        }));

        setSuggestions(formattedSuggestions);
        setShowSuggestions(true);
      } else {
        setSuggestions([]);
      }
    } catch (error) {
      console.error("Error fetching place suggestions:", error);
      setSuggestions([]);
    } finally {
      setIsLoading(false);
    }
  };

  const fetchPlaceDetails = async (placeId: string): Promise<PlaceAddressComponents> => {
    const defaults: PlaceAddressComponents = {
      street_number: "",
      street: "",
      city: "",
      province: "",
      region: "",
      postcode: "",
      country: "",
      country_code: "",
    };

    if (!apiKey || !placeId) return defaults;

    try {
      const response = await fetch(`https://places.googleapis.com/v1/places/${placeId}`, {
        headers: {
          "X-Goog-Api-Key": apiKey,
          "X-Goog-FieldMask": "addressComponents",
        },
      });

      if (!response.ok) return defaults;

      const data = await response.json();
      const components = data.addressComponents ?? [];

      const result = { ...defaults };

      for (const comp of components) {
        const types: string[] = comp.types ?? [];
        if (types.includes("street_number")) {
          result.street_number = comp.longText ?? "";
        } else if (types.includes("route")) {
          result.street = comp.longText ?? "";
        } else if (types.includes("locality")) {
          result.city = comp.longText ?? "";
        } else if (types.includes("administrative_area_level_2")) {
          result.province = comp.longText ?? "";
        } else if (types.includes("administrative_area_level_1")) {
          result.region = comp.longText ?? "";
        } else if (types.includes("postal_code")) {
          result.postcode = comp.longText ?? "";
        } else if (types.includes("country")) {
          result.country = comp.longText ?? "";
          result.country_code = comp.shortText ?? "";
        }
      }

      return result;
    } catch {
      return defaults;
    }
  };

  // Handle input changes with debouncing
  const handleInputChange = (value: string) => {
    setInputValue(value);
    // shouldValidate so a prior-submit "required" error on this field clears as
    // soon as it has a value (form.setValue alone does not re-run validation).
    form.setValue(id, value, { shouldValidate: true });

    if (debounceRef.current) {
      clearTimeout(debounceRef.current);
    }

    if (value.length > 2 && apiKey) {
      debounceRef.current = setTimeout(() => {
        fetchSuggestions(value);
      }, 300);
    } else {
      setSuggestions([]);
      setShowSuggestions(false);
      setIsLoading(false);
    }
  };

  // Handle suggestion selection
  const handleSuggestionSelect = async (suggestion: PlaceSuggestion) => {
    setInputValue(suggestion.description);
    form.setValue(id, suggestion.description, { shouldValidate: true });
    setShowSuggestions(false);
    setSuggestions([]);

    const addressComponents = await fetchPlaceDetails(suggestion.place_id);
    const enrichedSuggestion: PlaceSuggestion = {
      ...suggestion,
      addressComponents,
    };

    if (onPlaceSelect) {
      onPlaceSelect(enrichedSuggestion);
    }
  };

  // Close suggestions when clicking outside
  useEffect(() => {
    const handleClickOutside = (event: MouseEvent) => {
      if (containerRef.current && !containerRef.current.contains(event.target as Node)) {
        setShowSuggestions(false);
      }
    };

    document.addEventListener("mousedown", handleClickOutside);
    return () => {
      document.removeEventListener("mousedown", handleClickOutside);
    };
  }, []);

  // Cleanup debounce on unmount
  useEffect(() => {
    return () => {
      if (debounceRef.current) {
        clearTimeout(debounceRef.current);
      }
    };
  }, []);

  // Fallback to regular input if API key is not available
  if (loadError) {
    return (
      <div className="flex w-full flex-col">
        <FormFieldWrapper form={form} name={id} label={name} description={description} isRequired={isRequired}>
          {(field) => (
            <Input
              {...field}
              placeholder={placeholder}
              disabled={disabled}
              data-testid={testId}
              className={cn("w-full", className)}
            />
          )}
        </FormFieldWrapper>
      </div>
    );
  }

  return (
    <div className="flex w-full flex-col" ref={containerRef}>
      <FormFieldWrapper form={form} name={id} label={name} description={description} isRequired={isRequired}>
        {(field) => (
          <div className="relative">
            <Input
              value={inputValue}
              onChange={(e) => handleInputChange(e.target.value)}
              onBlur={field.onBlur}
              onFocus={() => {
                if (suggestions.length > 0) {
                  setShowSuggestions(true);
                }
              }}
              placeholder={placeholder}
              disabled={disabled || !apiKey}
              data-testid={testId}
              className={cn("w-full", className)}
            />

            {/* Loading indicator */}
            {isLoading && (
              <div className="absolute right-3 top-1/2 -translate-y-1/2">
                <div className="border-primary h-4 w-4 animate-spin rounded-full border-2 border-t-transparent"></div>
              </div>
            )}

            {/* Suggestions dropdown */}
            {showSuggestions && suggestions.length > 0 && (
              <div className="bg-background absolute left-0 right-0 top-full z-50 mt-1 max-h-60 overflow-auto rounded-md border shadow-lg">
                {suggestions.map((suggestion, index) => (
                  <div
                    key={suggestion.place_id || index}
                    className="hover:bg-muted cursor-pointer px-3 py-2 text-sm"
                    onClick={() => handleSuggestionSelect(suggestion)}
                  >
                    <div className="font-medium">{suggestion.structured_formatting?.main_text}</div>
                    <div className="text-muted-foreground">{suggestion.structured_formatting?.secondary_text}</div>
                  </div>
                ))}
              </div>
            )}
          </div>
        )}
      </FormFieldWrapper>
    </div>
  );
}
