
CREATE OR REPLACE FUNCTION public.search_addresses(search_term text, result_limit integer DEFAULT 10)
 RETURNS TABLE(identifiant_unique_adresse text, adresse_formatee text, numero_municipal text, numero_unite text, code_postal text, odonyme_recompose_normal text, nom_municipalite text, longitude double precision, latitude double precision, fiber_available boolean, zone_tarifaire integer, max_speed integer, similarity_score real)
 LANGUAGE plpgsql
 SECURITY DEFINER
 SET search_path TO 'public'
AS $function$
DECLARE
  clean_term text;
  num_part text;
  street_part text;
BEGIN
  clean_term := trim(search_term);
  
  -- Extract leading number and street part
  num_part := (regexp_match(clean_term, '^\s*(\d+)'))[1];
  street_part := trim(regexp_replace(clean_term, '^\s*\d+\s*', ''));

  -- Phase 1: If we have a number, filter by numero_municipal prefix first (very fast with btree)
  -- Then rank by street similarity if street_part exists
  IF num_part IS NOT NULL AND length(num_part) >= 1 THEN
    RETURN QUERY
    SELECT
      a.identifiant_unique_adresse,
      a.adresse_formatee,
      a.numero_municipal,
      a.numero_unite,
      a.code_postal,
      a.odonyme_recompose_normal,
      a.nom_municipalite,
      a.longitude,
      a.latitude,
      (f.id IS NOT NULL) AS fiber_available,
      f.zone_tarifaire,
      f.max_speed,
      CASE 
        WHEN street_part = '' THEN 
          CASE WHEN a.numero_municipal = num_part THEN 1.0 ELSE 0.5 END::real
        ELSE
          similarity(COALESCE(a.odonyme_recompose_normal, ''), street_part)
      END AS similarity_score
    FROM public.addresses a
    LEFT JOIN public.fiber_availability f ON f.uuidadresse = a.identifiant_unique_adresse
    WHERE a.numero_municipal = num_part
      AND (street_part = '' OR COALESCE(a.odonyme_recompose_normal, '') % street_part)
    ORDER BY similarity_score DESC
    LIMIT result_limit;
    
    -- If we got results, return them
    IF FOUND THEN
      RETURN;
    END IF;
    
    -- Fallback: prefix match on numero_municipal (e.g. user typed "193")
    RETURN QUERY
    SELECT
      a.identifiant_unique_adresse,
      a.adresse_formatee,
      a.numero_municipal,
      a.numero_unite,
      a.code_postal,
      a.odonyme_recompose_normal,
      a.nom_municipalite,
      a.longitude,
      a.latitude,
      (f.id IS NOT NULL) AS fiber_available,
      f.zone_tarifaire,
      f.max_speed,
      CASE 
        WHEN street_part = '' THEN 0.5::real
        ELSE similarity(COALESCE(a.odonyme_recompose_normal, ''), street_part)
      END AS similarity_score
    FROM public.addresses a
    LEFT JOIN public.fiber_availability f ON f.uuidadresse = a.identifiant_unique_adresse
    WHERE a.numero_municipal LIKE (num_part || '%')
      AND (street_part = '' OR COALESCE(a.odonyme_recompose_normal, '') % street_part)
    ORDER BY similarity_score DESC, a.numero_municipal
    LIMIT result_limit;
    
    IF FOUND THEN
      RETURN;
    END IF;
  END IF;

  -- Phase 2: No number found or no results — full trigram search on street/address
  RETURN QUERY
  SELECT
    a.identifiant_unique_adresse,
    a.adresse_formatee,
    a.numero_municipal,
    a.numero_unite,
    a.code_postal,
    a.odonyme_recompose_normal,
    a.nom_municipalite,
    a.longitude,
    a.latitude,
    (f.id IS NOT NULL) AS fiber_available,
    f.zone_tarifaire,
    f.max_speed,
    GREATEST(
      similarity(COALESCE(a.odonyme_recompose_normal, ''), clean_term),
      similarity(COALESCE(a.adresse_formatee, ''), clean_term)
    ) AS similarity_score
  FROM public.addresses a
  LEFT JOIN public.fiber_availability f ON f.uuidadresse = a.identifiant_unique_adresse
  WHERE COALESCE(a.odonyme_recompose_normal, '') % clean_term
     OR a.adresse_formatee % clean_term
  ORDER BY similarity_score DESC
  LIMIT result_limit;
END;
$function$;

-- Btree index on numero_municipal for fast exact/prefix matching
CREATE INDEX IF NOT EXISTS idx_addresses_numero_municipal 
ON public.addresses (numero_municipal);
