-- Prefijo del restaurante antes del numero de pedido (ej. APR-0001).
-- Se deriva automaticamente de las iniciales del nombre; cada restaurante nuevo lo hereda.

alter table public.restaurantes add column if not exists prefijo text;

-- Deriva un prefijo del nombre: iniciales de las palabras (>=2), si no, primeras 3 letras.
create or replace function public.derivar_prefijo(p_nombre text)
returns text
language plpgsql
immutable
as $$
declare
  v_ini text := '';
  w text;
begin
  for w in select regexp_replace(x, '[^a-zA-Z]', '', 'g') from regexp_split_to_table(coalesce(p_nombre, ''), '\s+') x loop
    if length(w) > 0 then v_ini := v_ini || upper(left(w, 1)); end if;
  end loop;
  if length(v_ini) >= 2 then
    return left(v_ini, 4);
  end if;
  return upper(left(regexp_replace(coalesce(p_nombre, ''), '[^a-zA-Z]', '', 'g'), 3));
end;
$$;

-- Poblar el prefijo de los restaurantes que no lo tienen.
update public.restaurantes set prefijo = derivar_prefijo(nombre)
 where prefijo is null or prefijo = '';

-- Trigger: cualquier restaurante nuevo (o al cambiar nombre si aun no tiene prefijo) lo obtiene solo.
create or replace function public.tg_prefijo_restaurante()
returns trigger
language plpgsql
set search_path to 'public'
as $$
begin
  if new.prefijo is null or new.prefijo = '' then
    new.prefijo := derivar_prefijo(new.nombre);
  end if;
  return new;
end;
$$;
drop trigger if exists trg_prefijo_restaurante on public.restaurantes;
create trigger trg_prefijo_restaurante
before insert or update on public.restaurantes
for each row execute function public.tg_prefijo_restaurante();

-- Renumerar los pedidos existentes con prefijo (por restaurante, orden de llegada).
with num as (
  select p.id, r.prefijo, row_number() over (partition by p.restaurante_id order by p.recibido_en, p.id) rn
  from public.pedidos p
  join public.restaurantes r on r.id = p.restaurante_id
)
update public.pedidos p
   set codigo = coalesce(nullif(num.prefijo, ''), 'GX') || '-' || lpad(num.rn::text, 4, '0')
  from num
 where num.id = p.id;

-- crear_pedido: codigo = PREFIJO-#### (numero consecutivo por restaurante).
CREATE OR REPLACE FUNCTION public.crear_pedido(p_restaurante_id uuid, p_cliente_nombre text, p_cliente_telefono text, p_tipo_entrega text, p_metodo_pago text, p_items jsonb, p_direccion text DEFAULT NULL::text, p_direccion_lat double precision DEFAULT NULL::double precision, p_direccion_lng double precision DEFAULT NULL::double precision, p_nota text DEFAULT NULL::text, p_mesa text DEFAULT NULL::text, p_cedula text DEFAULT NULL::text)
 RETURNS TABLE(pedido_id uuid, codigo text, cuenta_id uuid)
 LANGUAGE plpgsql
 SECURITY DEFINER
 SET search_path TO 'public'
AS $function$
declare
  v_rest           public.restaurantes%rowtype;
  v_item           jsonb;
  v_prod           public.productos%rowtype;
  v_cant           int;
  v_extra          numeric(10,2) := 0;
  v_subtotal       numeric(10,2) := 0;
  v_costo_delivery numeric(10,2) := 0;
  v_pedido_id      uuid;
  v_codigo         text;
  v_num            int;
  v_cuenta_id      uuid := null;
begin
  select * into v_rest from public.restaurantes where id = p_restaurante_id;
  if not found or not v_rest.activo or not v_rest.publicado then
    raise exception 'Restaurante no disponible';
  end if;
  if not v_rest.abierto then
    raise exception 'El restaurante esta cerrado en este momento';
  end if;
  if p_tipo_entrega not in ('delivery','pickup','mesa') then
    raise exception 'Tipo de entrega invalido';
  end if;
  if p_metodo_pago not in ('pago_movil','efectivo','transferencia','zelle','punto_venta') then
    raise exception 'Metodo de pago invalido';
  end if;
  if jsonb_array_length(coalesce(p_items, '[]'::jsonb)) = 0 then
    raise exception 'El carrito esta vacio';
  end if;

  if p_tipo_entrega = 'delivery' then
    v_costo_delivery := coalesce(v_rest.costo_delivery_usd, 0);
    if coalesce(length(trim(p_direccion)), 0) < 4 then
      raise exception 'Para delivery necesitas indicar la direccion';
    end if;
  end if;

  if p_tipo_entrega = 'mesa' then
    if coalesce(nullif(p_mesa, ''), '') = '' then
      raise exception 'Falta el numero de mesa';
    end if;
    insert into public.cuentas_mesa (restaurante_id, mesa)
      values (p_restaurante_id, p_mesa)
      on conflict (restaurante_id, mesa) where (estado <> 'cerrada') do nothing;
    select id into v_cuenta_id from public.cuentas_mesa
      where restaurante_id = p_restaurante_id and mesa = p_mesa and estado <> 'cerrada'
      limit 1;
  end if;

  -- Numero consecutivo por restaurante (el UPDATE bloquea la fila -> sin colisiones).
  update public.restaurantes set contador_pedidos = contador_pedidos + 1
   where id = p_restaurante_id
   returning contador_pedidos into v_num;
  v_codigo := coalesce(nullif(v_rest.prefijo, ''), 'GX') || '-' || lpad(v_num::text, 4, '0');

  insert into public.pedidos (
    restaurante_id, codigo, cliente_nombre, cliente_telefono, cliente_cedula, tipo_entrega, mesa,
    direccion, direccion_latitud, direccion_longitud, estado,
    subtotal_usd, costo_delivery_usd, total_usd, tasa_bs, metodo_pago, nota, cuenta_id
  ) values (
    p_restaurante_id, v_codigo, p_cliente_nombre, p_cliente_telefono, nullif(p_cedula, ''), p_tipo_entrega, nullif(p_mesa, ''),
    p_direccion, p_direccion_lat, p_direccion_lng, 'recibido',
    0, v_costo_delivery, 0, v_rest.tasa_bs, p_metodo_pago, p_nota, v_cuenta_id
  ) returning id into v_pedido_id;

  for v_item in select * from jsonb_array_elements(p_items)
  loop
    v_cant := greatest(1, coalesce((v_item->>'cantidad')::int, 1));
    select * into v_prod from public.productos
      where id = (v_item->>'producto_id')::uuid
        and restaurante_id = p_restaurante_id;
    if not found then
      raise exception 'Un producto no pertenece a este restaurante';
    end if;
    if not v_prod.disponible then
      raise exception 'Producto agotado: %', v_prod.nombre;
    end if;
    select coalesce(sum((op->>'precio_extra')::numeric), 0) into v_extra
      from jsonb_array_elements(coalesce(v_prod.opciones, '[]'::jsonb)) as grupo,
           jsonb_array_elements(coalesce(grupo->'opciones', '[]'::jsonb)) as op
     where jsonb_exists(coalesce(v_item->'opciones', '[]'::jsonb), op->>'nombre');
    insert into public.pedido_items (pedido_id, producto_id, nombre, precio_usd, cantidad, nota)
      values (v_pedido_id, v_prod.id, v_prod.nombre, v_prod.precio_usd + v_extra, v_cant, nullif(v_item->>'nota', ''));
    v_subtotal := v_subtotal + (v_prod.precio_usd + v_extra) * v_cant;
  end loop;

  update public.pedidos
     set subtotal_usd = v_subtotal,
         total_usd    = v_subtotal + v_costo_delivery
   where id = v_pedido_id;

  if p_tipo_entrega <> 'mesa' then
    insert into public.pagos (pedido_id, metodo, monto_usd, estado)
      values (v_pedido_id, p_metodo_pago, v_subtotal + v_costo_delivery, 'pendiente');
  end if;

  return query select v_pedido_id, v_codigo, v_cuenta_id;
end;
$function$;
