-- EL INTERRUPTOR FISCAL VIVE EN EL PANEL DE LA IMPRENTA, NO EN EL NEGOCIO
-- (2026-07-29, decision de Gilberto)
--
-- Regla dura: el dueno del restaurante NO puede encender ni apagar la
-- facturacion. Solo el proveedor autorizado (el panel de la imprenta) la
-- enciende, la suspende y la revoca.
--
-- Esto NO es solo control comercial: es requisito de homologacion. Una maquina
-- fiscal no tiene boton de "hoy no facturo". Si el sistema le diera al negocio
-- la opcion de apagar la emision y seguir vendiendo, eso seria justo lo que
-- prohibe el articulo 8 de la SNAT/2024/000121 (programas de contabilidad
-- alterna). Que no se pueda apagar desde el punto de venta es la respuesta que
-- se le da al fiscal cuando pregunte.
--
-- FRONTERA INTACTA: aca no se calcula, ni se muestra, ni se reporta un solo
-- impuesto. Esto solo dice SI ese negocio esta habilitado y por quien.
begin;

-- 1. EL VINCULO ------------------------------------------------------------
-- Un negocio de Gustito queda amarrado a un emisor dado de alta en la imprenta.
-- Sin fila aca, para Gustito la facturacion sencillamente no existe.
create table if not exists fiscal.negocios (
  restaurante_id uuid primary key references public.restaurantes(id) on delete cascade,
  emisor_id uuid not null references fiscal.emisores(id) on delete cascade,
  serie text not null default 'A',
  vinculado_en timestamptz not null default now(),
  vinculado_por text,
  nota text
);
create index if not exists fiscal_negocios_emisor_idx on fiscal.negocios(emisor_id);

revoke all on table fiscal.negocios from anon, authenticated;

-- 2. LA PREGUNTA QUE HACE GUSTITO -----------------------------------------
-- "Este negocio esta habilitado para facturar?" Responde si o no, y cuando es
-- que no, dice por que (para que el panel de la imprenta lo explique, no el
-- negocio). Devuelve solo lo que hace falta para armar el encabezado del
-- documento: nada de llaves, nada de montos, nada de impuestos.
create or replace function public.fn_estado_fiscal(p_restaurante_id uuid)
returns jsonb
language plpgsql
stable
security definer
set search_path to 'public', 'fiscal'
as $fn$
declare
  v_neg record;
  v_llaves int;
begin
  select n.serie, e.rif, e.razon_social, e.domicilio_fiscal, e.activo, e.rif_comprobado
    into v_neg
    from fiscal.negocios n
    join fiscal.emisores e on e.id = n.emisor_id
   where n.restaurante_id = p_restaurante_id;

  if not found then
    return jsonb_build_object('activa', false, 'motivo', 'sin_vinculo');
  end if;

  if not v_neg.activo then
    return jsonb_build_object('activa', false, 'motivo', 'suspendido');
  end if;

  -- Art. 29 de la 000102: no se le emite a quien no entrego su comprobante de RIF.
  if not v_neg.rif_comprobado then
    return jsonb_build_object('activa', false, 'motivo', 'sin_comprobante_rif');
  end if;

  select count(*) into v_llaves
    from fiscal.llaves l
    join fiscal.negocios n on n.emisor_id = l.emisor_id
   where n.restaurante_id = p_restaurante_id and l.activa;
  if v_llaves = 0 then
    return jsonb_build_object('activa', false, 'motivo', 'sin_llave');
  end if;

  return jsonb_build_object(
    'activa', true,
    'rif', v_neg.rif,
    'razon_social', v_neg.razon_social,
    'domicilio_fiscal', v_neg.domicilio_fiscal,
    'serie', v_neg.serie
  );
end;
$fn$;

grant execute on function public.fn_estado_fiscal(uuid) to anon, authenticated, service_role;

-- 3. EL CANDADO ------------------------------------------------------------
-- El dueno no tiene boton de apagado, pero podria intentar el atajo: borrarse
-- el RIF o cambiarselo para que el sistema no lo reconozca. Eso se bloquea
-- mientras este vinculado: los datos fiscales del emisor los mueve la imprenta.
create or replace function public.fn_proteger_datos_fiscales()
returns trigger
language plpgsql
security definer
set search_path to 'public', 'fiscal'
as $fn$
begin
  if new.rif is distinct from old.rif
     or new.razon_social is distinct from old.razon_social
     or new.domicilio_fiscal is distinct from old.domicilio_fiscal then
    if exists (select 1 from fiscal.negocios n
                join fiscal.emisores e on e.id = n.emisor_id
               where n.restaurante_id = old.id and e.activo) then
      raise exception 'Los datos fiscales de este negocio los administra el proveedor autorizado. Comunicate con el para cualquier cambio.';
    end if;
  end if;
  return new;
end;
$fn$;

drop trigger if exists tg_proteger_datos_fiscales on public.restaurantes;
create trigger tg_proteger_datos_fiscales
  before update on public.restaurantes
  for each row execute function public.fn_proteger_datos_fiscales();

-- 4. LA PALANCA, DESDE LA SILLA DE GILBERTO --------------------------------
create or replace function fiscal.vincular_negocio(
  p_restaurante_id uuid,
  p_rif text,
  p_serie text default null,
  p_quien text default null,
  p_nota text default null
)
returns jsonb
language plpgsql
security definer
set search_path to 'fiscal', 'public'
as $fn$
declare
  v_emisor record;
  v_rest   record;
begin
  select * into v_emisor from fiscal.emisores where rif = public.fn_rif_normalizado(p_rif);
  if not found then
    raise exception 'Ese RIF no esta dado de alta como cliente de la imprenta';
  end if;

  select id, nombre into v_rest from public.restaurantes where id = p_restaurante_id;
  if not found then
    raise exception 'No existe ese negocio en Gustito';
  end if;

  insert into fiscal.negocios (restaurante_id, emisor_id, serie, vinculado_por, nota)
    values (p_restaurante_id, v_emisor.id,
            upper(coalesce(nullif(trim(p_serie), ''), v_emisor.serie_asignada, 'A')),
            p_quien, p_nota)
    on conflict (restaurante_id) do update set
      emisor_id = excluded.emisor_id,
      serie = excluded.serie,
      vinculado_en = now(),
      vinculado_por = excluded.vinculado_por,
      nota = excluded.nota;

  -- El encabezado del documento sale de los datos del emisor, no de lo que el
  -- negocio haya escrito por su cuenta. Se copian una vez y quedan bajo llave
  -- (el trigger de arriba impide que los toque despues).
  update public.restaurantes
     set rif = v_emisor.rif,
         razon_social = v_emisor.razon_social,
         domicilio_fiscal = v_emisor.domicilio_fiscal
   where id = p_restaurante_id;

  return jsonb_build_object('negocio', v_rest.nombre, 'rif', v_emisor.rif);
end;
$fn$;

create or replace function fiscal.desvincular_negocio(p_restaurante_id uuid)
returns boolean
language sql
security definer
set search_path to 'fiscal', 'public'
as $fn$
  delete from fiscal.negocios where restaurante_id = p_restaurante_id returning true;
$fn$;

-- Los negocios de Gustito vistos desde el panel: cuales estan habilitados,
-- cuales no y por que.
create or replace function fiscal.negocios_gustito()
returns table(
  restaurante_id uuid,
  nombre text,
  rif text,
  razon_social text,
  serie text,
  vinculado_en timestamptz,
  activa boolean,
  motivo text
)
language sql
stable
security definer
set search_path to 'fiscal', 'public'
as $fn$
  select r.id, r.nombre, e.rif, e.razon_social, n.serie, n.vinculado_en,
         (public.fn_estado_fiscal(r.id)->>'activa')::boolean,
         public.fn_estado_fiscal(r.id)->>'motivo'
    from fiscal.negocios n
    join public.restaurantes r on r.id = n.restaurante_id
    join fiscal.emisores e on e.id = n.emisor_id
   order by r.nombre;
$fn$;

-- Buscador para dar de alta sin tener que andar copiando uuid a mano.
create or replace function fiscal.buscar_negocio(p_texto text)
returns table(id uuid, nombre text, slug text, rif text, ya_vinculado boolean)
language sql
stable
security definer
set search_path to 'fiscal', 'public'
as $fn$
  select r.id, r.nombre, r.slug, r.rif,
         exists (select 1 from fiscal.negocios n where n.restaurante_id = r.id)
    from public.restaurantes r
   where p_texto is not null and length(trim(p_texto)) >= 2
     and (r.nombre ilike '%' || trim(p_texto) || '%' or r.slug ilike '%' || trim(p_texto) || '%')
   order by r.nombre
   limit 20;
$fn$;

-- 5. LAS ACCIONES NUEVAS DEL PANEL ----------------------------------------
create or replace function public.fn_panel_imprenta(p_accion text, p_datos jsonb default '{}'::jsonb)
returns jsonb
language plpgsql
security definer
set search_path to 'public', 'fiscal'
as $fn$
declare v jsonb;
begin
  case p_accion
    when 'entrar' then
      select to_jsonb(t) into v from fiscal.entrar_panel(p_datos->>'usuario', p_datos->>'clave') t;
    when 'tablero' then
      select to_jsonb(t) into v from fiscal.tablero() t;
    when 'clientes' then
      select coalesce(jsonb_agg(to_jsonb(t)), '[]'::jsonb) into v from fiscal.clientes() t;
    when 'guardar_cliente' then
      select to_jsonb(fiscal.guardar_cliente(
        p_datos->>'rif', p_datos->>'razon_social', p_datos->>'domicilio',
        p_datos->>'representante', p_datos->>'cedula', p_datos->>'correo',
        p_datos->>'telefono', p_datos->>'sistema', coalesce(p_datos->>'serie', 'A'),
        coalesce((p_datos->>'rif_comprobado')::boolean, false), p_datos->>'nota')) into v;
    when 'activar_cliente' then
      select to_jsonb(fiscal.activar_cliente(p_datos->>'rif', (p_datos->>'activo')::boolean)) into v;
    when 'llaves' then
      select coalesce(jsonb_agg(to_jsonb(t)), '[]'::jsonb) into v from fiscal.llaves_de(p_datos->>'rif') t;
    when 'crear_llave' then
      select to_jsonb(fiscal.crear_llave(p_datos->>'rif', coalesce(p_datos->>'nombre', 'llave'))) into v;
    when 'revocar_llave' then
      select to_jsonb(fiscal.revocar_llave((p_datos->>'id')::uuid)) into v;
    when 'resumen_mensual' then
      select to_jsonb(t) into v from fiscal.resumen_mensual(
        p_datos->>'rif', (p_datos->>'anio')::int, (p_datos->>'mes')::int) t;
    when 'relacion_mensual' then
      select coalesce(jsonb_agg(to_jsonb(t)), '[]'::jsonb) into v from fiscal.relacion_mensual(
        p_datos->>'rif', (p_datos->>'anio')::int, (p_datos->>'mes')::int) t;
    when 'marcar_enviado' then
      select to_jsonb(fiscal.marcar_enviado(
        p_datos->>'rif', (p_datos->>'anio')::int, (p_datos->>'mes')::int,
        p_datos->>'quien', p_datos->>'nota')) into v;
    -- Negocios de Gustito: el interruptor que el dueno no tiene.
    when 'negocios_gustito' then
      select coalesce(jsonb_agg(to_jsonb(t)), '[]'::jsonb) into v from fiscal.negocios_gustito() t;
    when 'buscar_negocio' then
      select coalesce(jsonb_agg(to_jsonb(t)), '[]'::jsonb) into v from fiscal.buscar_negocio(p_datos->>'texto') t;
    when 'vincular_negocio' then
      select fiscal.vincular_negocio((p_datos->>'restaurante_id')::uuid, p_datos->>'rif',
                                     p_datos->>'serie', p_datos->>'quien', p_datos->>'nota') into v;
    when 'desvincular_negocio' then
      select to_jsonb(fiscal.desvincular_negocio((p_datos->>'restaurante_id')::uuid)) into v;
    else
      raise exception 'Accion desconocida';
  end case;
  return coalesce(v, 'null'::jsonb);
end;
$fn$;

revoke execute on function public.fn_panel_imprenta(text, jsonb) from anon, authenticated, public;
grant execute on function public.fn_panel_imprenta(text, jsonb) to service_role;

commit;
