-- ============================================================
-- Gustito Express — Esquema inicial (multi-negocio + RLS)
-- Convencion: espanol snake_case, timestamps con sufijo _en, GPS latitud/longitud.
-- ============================================================

create extension if not exists pgcrypto;

-- ------------------------------------------------------------
-- Utilidad: mantener actualizado_en
-- ------------------------------------------------------------
create or replace function public.set_actualizado_en()
returns trigger
language plpgsql
as $$
begin
  new.actualizado_en := now();
  return new;
end;
$$;

-- ------------------------------------------------------------
-- Tablas
-- ------------------------------------------------------------

create table public.restaurantes (
  id                 uuid primary key default gen_random_uuid(),
  slug               text not null unique,
  nombre             text not null,
  descripcion        text,
  logo_url           text,
  portada_url        text,
  color_primario     text not null default '#F97316',
  telefono           text,
  direccion          text,
  latitud            double precision,
  longitud           double precision,
  tasa_bs            numeric(12,2),
  tasa_automatica    boolean not null default true,
  pago_movil_datos   jsonb,
  metodos_pago       jsonb not null default '{}'::jsonb,
  costo_delivery_usd numeric(10,2) not null default 0,
  hace_delivery      boolean not null default true,
  hace_pickup        boolean not null default true,
  delivery_modo      text not null default 'fijo' check (delivery_modo in ('fijo','zona','distancia')),
  delivery_radio_km  numeric(6,2),
  horarios           jsonb,
  abierto            boolean not null default true,
  activo             boolean not null default true,
  publicado          boolean not null default false,
  onboarding_completo boolean not null default false,
  creado_en          timestamptz not null default now(),
  actualizado_en     timestamptz not null default now()
);
create index idx_restaurantes_slug on public.restaurantes (slug) where activo;

create table public.perfiles (
  id             uuid primary key references auth.users (id) on delete cascade,
  rol            text not null default 'comensal'
                   check (rol in ('dueno','staff','repartidor','comensal','admin')),
  restaurante_id uuid references public.restaurantes (id) on delete set null,
  nombre         text,
  telefono       text,
  creado_en      timestamptz not null default now()
);
create index idx_perfiles_restaurante on public.perfiles (restaurante_id);

create table public.categorias (
  id             uuid primary key default gen_random_uuid(),
  restaurante_id uuid not null references public.restaurantes (id) on delete cascade,
  nombre         text not null,
  orden          int not null default 0,
  creado_en      timestamptz not null default now()
);
create index idx_categorias_restaurante on public.categorias (restaurante_id, orden);

create table public.productos (
  id             uuid primary key default gen_random_uuid(),
  restaurante_id uuid not null references public.restaurantes (id) on delete cascade,
  categoria_id   uuid references public.categorias (id) on delete set null,
  nombre         text not null,
  descripcion    text,
  precio_usd     numeric(10,2) not null check (precio_usd >= 0),
  foto_url       text,
  disponible     boolean not null default true,
  orden          int not null default 0,
  creado_en      timestamptz not null default now(),
  actualizado_en timestamptz not null default now()
);
create index idx_productos_restaurante on public.productos (restaurante_id, orden);
create index idx_productos_categoria on public.productos (categoria_id);

create table public.repartidores (
  id                  uuid primary key default gen_random_uuid(),
  perfil_id           uuid not null references public.perfiles (id) on delete cascade,
  restaurante_id      uuid not null references public.restaurantes (id) on delete cascade,
  vehiculo            text,
  estado              text not null default 'disponible'
                        check (estado in ('disponible','ocupado','offline')),
  ultima_latitud      double precision,
  ultima_longitud     double precision,
  ultima_ubicacion_en timestamptz,
  creado_en           timestamptz not null default now(),
  unique (perfil_id, restaurante_id)
);
create index idx_repartidores_restaurante on public.repartidores (restaurante_id, estado);

-- Codigo legible de pedido: FF-00001
create sequence if not exists public.pedido_codigo_seq;

create table public.pedidos (
  id                 uuid primary key default gen_random_uuid(),
  restaurante_id     uuid not null references public.restaurantes (id) on delete cascade,
  codigo             text not null unique,
  cliente_nombre     text not null,
  cliente_telefono   text not null,
  cliente_cedula     text,
  comensal_id        uuid references public.perfiles (id) on delete set null,
  tipo_entrega       text not null check (tipo_entrega in ('delivery','pickup','mesa')),
  mesa               text,
  direccion          text,
  direccion_latitud  double precision,
  direccion_longitud double precision,
  estado             text not null default 'recibido'
                       check (estado in ('recibido','confirmado','preparando','listo','en_camino','entregado','cancelado')),
  subtotal_usd       numeric(10,2) not null,
  costo_delivery_usd numeric(10,2) not null default 0,
  total_usd          numeric(10,2) not null,
  tasa_bs            numeric(12,2),
  metodo_pago        text not null check (metodo_pago in ('pago_movil','efectivo','transferencia','zelle')),
  nota               text,
  repartidor_id      uuid references public.repartidores (id) on delete set null,
  recibido_en        timestamptz not null default now(),
  confirmado_en      timestamptz,
  listo_en           timestamptz,
  en_camino_en       timestamptz,
  entregado_en       timestamptz,
  cancelado_en       timestamptz,
  actualizado_en     timestamptz not null default now()
);
create index idx_pedidos_restaurante_estado on public.pedidos (restaurante_id, estado, recibido_en desc);
create index idx_pedidos_repartidor on public.pedidos (repartidor_id) where repartidor_id is not null;

create table public.pedido_items (
  id          uuid primary key default gen_random_uuid(),
  pedido_id   uuid not null references public.pedidos (id) on delete cascade,
  producto_id uuid references public.productos (id) on delete set null,
  nombre      text not null,
  precio_usd  numeric(10,2) not null,
  cantidad    int not null check (cantidad > 0),
  nota        text
);
create index idx_pedido_items_pedido on public.pedido_items (pedido_id);

create table public.pagos (
  id             uuid primary key default gen_random_uuid(),
  pedido_id      uuid not null references public.pedidos (id) on delete cascade,
  metodo         text not null check (metodo in ('pago_movil','efectivo','transferencia','zelle')),
  monto_usd      numeric(10,2) not null,
  referencia     text,
  comprobante_url text,
  estado         text not null default 'pendiente'
                   check (estado in ('pendiente','verificando','confirmado','rechazado')),
  confirmado_por uuid references public.perfiles (id) on delete set null,
  confirmado_en  timestamptz,
  creado_en      timestamptz not null default now()
);
create index idx_pagos_pedido on public.pagos (pedido_id);

-- Triggers actualizado_en
create trigger trg_restaurantes_upd before update on public.restaurantes
  for each row execute function public.set_actualizado_en();
create trigger trg_productos_upd before update on public.productos
  for each row execute function public.set_actualizado_en();
create trigger trg_pedidos_upd before update on public.pedidos
  for each row execute function public.set_actualizado_en();

-- ------------------------------------------------------------
-- Helpers para RLS (SECURITY DEFINER, evitan recursion sobre perfiles)
-- ------------------------------------------------------------
create or replace function public.fn_es_admin()
returns boolean language sql stable security definer set search_path = public as $$
  select exists (select 1 from public.perfiles where id = auth.uid() and rol = 'admin');
$$;

create or replace function public.fn_mi_restaurante()
returns uuid language sql stable security definer set search_path = public as $$
  select restaurante_id from public.perfiles where id = auth.uid();
$$;

create or replace function public.fn_es_staff_de(p_restaurante uuid)
returns boolean language sql stable security definer set search_path = public as $$
  select exists (
    select 1 from public.perfiles
    where id = auth.uid()
      and restaurante_id = p_restaurante
      and rol in ('dueno','staff')
  );
$$;

-- ------------------------------------------------------------
-- RLS
-- ------------------------------------------------------------
alter table public.restaurantes  enable row level security;
alter table public.perfiles      enable row level security;
alter table public.categorias    enable row level security;
alter table public.productos     enable row level security;
alter table public.repartidores  enable row level security;
alter table public.pedidos       enable row level security;
alter table public.pedido_items  enable row level security;
alter table public.pagos         enable row level security;

-- restaurantes: catalogo publico (solo activos); staff/admin gestionan el suyo
create policy restaurantes_lectura_publica on public.restaurantes
  for select to anon, authenticated using (activo);
create policy restaurantes_admin_todo on public.restaurantes
  for all to authenticated using (fn_es_admin()) with check (fn_es_admin());
create policy restaurantes_staff_update on public.restaurantes
  for update to authenticated using (fn_es_staff_de(id)) with check (fn_es_staff_de(id));

-- perfiles: cada quien ve/edita el suyo; admin todo
create policy perfiles_propio_select on public.perfiles
  for select to authenticated using (id = auth.uid() or fn_es_admin());
create policy perfiles_propio_update on public.perfiles
  for update to authenticated using (id = auth.uid()) with check (id = auth.uid());
create policy perfiles_admin_todo on public.perfiles
  for all to authenticated using (fn_es_admin()) with check (fn_es_admin());

-- categorias / productos: lectura publica; escritura staff del restaurante o admin
create policy categorias_lectura_publica on public.categorias
  for select to anon, authenticated using (true);
create policy categorias_staff_escribe on public.categorias
  for all to authenticated
  using (fn_es_staff_de(restaurante_id) or fn_es_admin())
  with check (fn_es_staff_de(restaurante_id) or fn_es_admin());

create policy productos_lectura_publica on public.productos
  for select to anon, authenticated using (true);
create policy productos_staff_escribe on public.productos
  for all to authenticated
  using (fn_es_staff_de(restaurante_id) or fn_es_admin())
  with check (fn_es_staff_de(restaurante_id) or fn_es_admin());

-- repartidores: staff del restaurante o admin gestionan; el repartidor ve/actualiza el suyo
create policy repartidores_staff on public.repartidores
  for all to authenticated
  using (fn_es_staff_de(restaurante_id) or fn_es_admin())
  with check (fn_es_staff_de(restaurante_id) or fn_es_admin());
create policy repartidores_propio_select on public.repartidores
  for select to authenticated using (perfil_id = auth.uid());
create policy repartidores_propio_update on public.repartidores
  for update to authenticated using (perfil_id = auth.uid()) with check (perfil_id = auth.uid());

-- pedidos: NO hay acceso anon directo (el comensal crea/consulta via RPC SECURITY DEFINER).
-- staff del restaurante ve los suyos; el repartidor ve los que tiene asignados; admin todo.
create policy pedidos_staff_select on public.pedidos
  for select to authenticated using (fn_es_staff_de(restaurante_id) or fn_es_admin());
create policy pedidos_repartidor_select on public.pedidos
  for select to authenticated
  using (repartidor_id in (select id from public.repartidores where perfil_id = auth.uid()));

create policy pedido_items_staff_select on public.pedido_items
  for select to authenticated using (
    exists (select 1 from public.pedidos p
            where p.id = pedido_items.pedido_id
              and (fn_es_staff_de(p.restaurante_id) or fn_es_admin()))
  );

create policy pagos_staff_select on public.pagos
  for select to authenticated using (
    exists (select 1 from public.pedidos p
            where p.id = pagos.pedido_id
              and (fn_es_staff_de(p.restaurante_id) or fn_es_admin()))
  );
