-- Paso 1
drop text search configuration if exists public.es_simple_unaccent cascade;

create text search configuration public.es_simple_unaccent (copy = pg_catalog.simple);

alter text search configuration public.es_simple_unaccent
  alter mapping for hword, hword_part, word
  with unaccent, simple;

-- Paso 2
alter table public.asignaturas
add column if not exists search_vector tsvector;

-- Paso 3
create or replace function public.fn_asignaturas_update_search_vector()
returns trigger
language plpgsql
as $$
begin
  new.search_vector :=
      setweight(
        to_tsvector('public.es_simple_unaccent', coalesce(new.nombre, '')),
        'A'
      )
      ||
      setweight(
        to_tsvector('public.es_simple_unaccent', coalesce(new.codigo, '')),
        'A'
      )
      ||
      setweight(
        to_tsvector('public.es_simple_unaccent', coalesce(new.datos, '{}'::jsonb)::text),
        'B'
      )
      ||
      setweight(
        to_tsvector('public.es_simple_unaccent', coalesce(new.contenido_tematico, '[]'::jsonb)::text),
        'B'
      );

  return new;
end;
$$;

-- Paso 4
drop trigger if exists trg_asignaturas_search_vector on public.asignaturas;

create trigger trg_asignaturas_search_vector
before insert or update of nombre, codigo, datos, contenido_tematico
on public.asignaturas
for each row
execute function public.fn_asignaturas_update_search_vector();

-- Paso 5
update public.asignaturas
set search_vector =
    setweight(
      to_tsvector('public.es_simple_unaccent', coalesce(nombre, '')),
      'A'
    )
    ||
    setweight(
      to_tsvector('public.es_simple_unaccent', coalesce(codigo, '')),
      'A'
    )
    ||
    setweight(
      to_tsvector('public.es_simple_unaccent', coalesce(datos, '{}'::jsonb)::text),
      'B'
    )
    ||
    setweight(
      to_tsvector('public.es_simple_unaccent', coalesce(contenido_tematico, '[]'::jsonb)::text),
      'B'
    );

-- Paso 6
create index if not exists asignaturas_search_vector_gin_idx
on public.asignaturas
using gin (search_vector);

-- Paso 7
create or replace function public.build_asignaturas_prefix_tsquery(p_search text)
returns tsquery
language plpgsql
stable
as $$
declare
  cleaned text;
  tokens text[];
  query_text text;
begin
  cleaned := trim(coalesce(p_search, ''));

  if cleaned = '' then
    return null;
  end if;

  cleaned := lower(public.unaccent(cleaned));
  cleaned := regexp_replace(cleaned, '[^[:alnum:]\s]+', ' ', 'g');
  cleaned := regexp_replace(cleaned, '\s+', ' ', 'g');

  tokens := regexp_split_to_array(cleaned, '\s+');

  select string_agg(token || ':*', ' & ')
  into query_text
  from unnest(tokens) as token
  where token <> '';

  if query_text is null or query_text = '' then
    return null;
  end if;

  return to_tsquery('public.es_simple_unaccent', query_text);
end;
$$;

-- Paso 8
create or replace function public.search_asignaturas(
  p_search text,
  p_plan_estudio_id uuid default null,
  p_limit integer default 20,
  p_offset integer default 0
)
returns table (
  id uuid,
  plan_estudio_id uuid,
  codigo text,
  nombre text,
  tipo public.tipo_asignatura,
  creditos numeric,
  numero_ciclo integer,
  datos jsonb,
  contenido_tematico jsonb,
  estado public.estado_asignatura,
  rank real
)
language sql
stable
as $$
  with q as (
    select public.build_asignaturas_prefix_tsquery(p_search) as tsq
  )
  select
    a.id,
    a.plan_estudio_id,
    a.codigo,
    a.nombre,
    a.tipo,
    a.creditos,
    a.numero_ciclo,
    a.datos,
    a.contenido_tematico,
    a.estado,
    ts_rank(a.search_vector, q.tsq) as rank
  from public.asignaturas a
  cross join q
  where
    q.tsq is not null
    and a.search_vector @@ q.tsq
    and (p_plan_estudio_id is null or a.plan_estudio_id = p_plan_estudio_id)
  order by
    rank desc,
    a.nombre asc
  limit p_limit
  offset p_offset;
$$;