-- ART. 33 DE LA 000102: "Las imprentas digitales autorizadas estan obligadas a
-- subsanar, sin costo alguno, los errores u omisiones". Hasta hoy no existia
-- donde dejar constancia de un reclamo ni de su correccion. Este registro
-- guarda que se reclamo, cuando, quien lo atendio y como se corrigio; el
-- costo cero es condicion de la norma, no una opcion del panel.

create table if not exists fiscal.subsanaciones (
  id uuid primary key default gen_random_uuid(),
  emisor_id uuid not null references fiscal.emisores(id),
  -- Referencia libre al documento afectado (numero de control o serie-numero),
  -- vacia si el reclamo es del servicio y no de un documento puntual.
  documento text,
  descripcion text not null,
  estado text not null default 'abierta' check (estado in ('abierta', 'subsanada')),
  abierto_en timestamptz not null default now(),
  abierto_por text not null,
  resuelto_en timestamptz,
  resuelto_por text,
  resolucion text
);

create index if not exists subsanaciones_emisor on fiscal.subsanaciones (emisor_id, abierto_en desc);

create or replace function fiscal.abrir_subsanacion(
  p_rif text, p_documento text, p_descripcion text, p_quien text
) returns uuid
language plpgsql
security definer
set search_path to 'fiscal', 'public'
as $$
declare
  v_emisor uuid;
  v_id uuid;
begin
  select id into v_emisor from fiscal.emisores where rif = public.fn_rif_normalizado(p_rif);
  if v_emisor is null then
    raise exception 'Ese RIF no esta registrado como emisor';
  end if;
  if coalesce(trim(p_descripcion), '') = '' then
    raise exception 'Describa el error u omision reclamado';
  end if;
  insert into fiscal.subsanaciones (emisor_id, documento, descripcion, abierto_por)
    values (v_emisor, nullif(trim(coalesce(p_documento, '')), ''), trim(p_descripcion), p_quien)
  returning id into v_id;
  return v_id;
end;
$$;

create or replace function fiscal.cerrar_subsanacion(
  p_id uuid, p_resolucion text, p_quien text
) returns boolean
language plpgsql
security definer
set search_path to 'fiscal', 'public'
as $$
begin
  if coalesce(trim(p_resolucion), '') = '' then
    raise exception 'Indique como se corrigio el error u omision';
  end if;
  update fiscal.subsanaciones
     set estado = 'subsanada',
         resuelto_en = now(),
         resuelto_por = p_quien,
         resolucion = trim(p_resolucion)
   where id = p_id and estado = 'abierta';
  if not found then
    raise exception 'Esa subsanacion no existe o ya fue cerrada';
  end if;
  return true;
end;
$$;

create or replace function fiscal.subsanaciones_lista()
returns table(
  id uuid, rif text, razon_social text, documento text, descripcion text,
  estado text, abierto_en timestamptz, abierto_por text,
  resuelto_en timestamptz, resuelto_por text, resolucion text
)
language sql
stable
security definer
set search_path to 'fiscal', 'public'
as $$
  select s.id, e.rif, e.razon_social, s.documento, s.descripcion,
         s.estado, s.abierto_en, s.abierto_por,
         s.resuelto_en, s.resuelto_por, s.resolucion
    from fiscal.subsanaciones s
    join fiscal.emisores e on e.id = s.emisor_id
   order by (s.estado = 'abierta') desc, s.abierto_en desc;
$$;

-- El despachador del panel gana las tres acciones (mismo retorno, CREATE OR
-- REPLACE basta; el resto de los casos identicos a la migracion 20260731090000
-- del vencimiento del RIF).
CREATE OR REPLACE FUNCTION public.fn_panel_imprenta(p_accion text, p_datos jsonb DEFAULT '{}'::jsonb, p_quien text DEFAULT NULL::text, p_desde text DEFAULT NULL::text, p_yo text DEFAULT NULL::text)
 RETURNS jsonb
 LANGUAGE plpgsql
 SECURITY DEFINER
 SET search_path TO 'public', 'fiscal'
AS $function$
declare
  v jsonb;
  v_quien text := coalesce(nullif(btrim(p_quien), ''), 'sin identificar');
  v_viejo jsonb;
begin
  case p_accion
    -- ===== NUMERACION DE LA IMPRENTA (lo nuevo) =====
    when 'numeracion' then
      -- Todo lo que la imprenta necesita ver de un vistazo.
      select jsonb_build_object(
        'resumen', fiscal.resumen_numeracion(),
        'rangos', coalesce((select jsonb_agg(to_jsonb(r) order by r.desde)
                              from (select id, desde, hasta, oficio, asignado_en, anulado, nota,
                                           (hasta - desde + 1) as cuantos
                                      from fiscal.rangos_imprenta) r), '[]'::jsonb),
        'clientes', coalesce((select jsonb_agg(to_jsonb(t)) from fiscal.rangos_de_clientes() t), '[]'::jsonb)
      ) into v;
    when 'guardar_imprenta' then
      select fiscal.guardar_imprenta(
        p_datos->>'rif', p_datos->>'razon_social', p_datos->>'domicilio',
        p_datos->>'providencia', nullif(p_datos->>'providencia_fecha','')::date,
        nullif(p_datos->>'vigencia_hasta','')::date,
        coalesce((p_datos->>'autorizada')::boolean, false)) into v;
      perform fiscal.anotar_panel(v_quien, p_desde, 'imprenta_guardada',
        jsonb_build_object('providencia', p_datos->>'providencia',
                           'autorizada', coalesce((p_datos->>'autorizada')::boolean, false)));
    when 'cargar_rango' then
      select fiscal.cargar_rango_imprenta(
        (p_datos->>'desde')::bigint, (p_datos->>'hasta')::bigint,
        p_datos->>'oficio', p_datos->>'nota') into v;
      perform fiscal.anotar_panel(v_quien, p_desde, 'rango_cargado',
        jsonb_build_object('desde', p_datos->>'desde', 'hasta', p_datos->>'hasta',
                           'oficio', p_datos->>'oficio'));
    when 'entregar_rango' then
      select fiscal.entregar_rango(
        p_datos->>'rif', (p_datos->>'cuantos')::bigint,
        nullif(p_datos->>'rango_imprenta','')::uuid) into v;
      perform fiscal.anotar_panel(v_quien, p_desde, 'rango_entregado',
        jsonb_build_object('rif', p_datos->>'rif', 'cuantos', p_datos->>'cuantos',
                           'desde', v->>'desde', 'hasta', v->>'hasta'));

    -- ===== SUBSANACIONES (art. 33 de la 000102) =====
    when 'subsanaciones' then
      select coalesce(jsonb_agg(to_jsonb(t)), '[]'::jsonb) into v
        from fiscal.subsanaciones_lista() t;
    when 'abrir_subsanacion' then
      select to_jsonb(fiscal.abrir_subsanacion(
        p_datos->>'rif', p_datos->>'documento', p_datos->>'descripcion', v_quien)) into v;
      perform fiscal.anotar_panel(v_quien, p_desde, 'subsanacion_abierta',
        jsonb_build_object('rif', p_datos->>'rif', 'documento', p_datos->>'documento'));
    when 'cerrar_subsanacion' then
      select to_jsonb(fiscal.cerrar_subsanacion(
        (p_datos->>'id')::uuid, p_datos->>'resolucion', v_quien)) into v;
      perform fiscal.anotar_panel(v_quien, p_desde, 'subsanacion_cerrada',
        jsonb_build_object('id', p_datos->>'id'));

    -- ===== LO QUE YA EXISTIA =====
    when 'entrar' then
      select to_jsonb(t) into v from fiscal.entrar_panel(
        p_datos->>'usuario', p_datos->>'clave', p_datos->>'desde') t;
    when 'tablero' then
      select to_jsonb(t) into v from fiscal.tablero() t;
    when 'salud_reportes' then
      -- Art. 29 num. 5 y art. 34: meses con documentos numerados y su estado
      -- de remision al SENIAT, para el aviso del panel.
      select coalesce(jsonb_agg(to_jsonb(t)), '[]'::jsonb) into v
        from fiscal.salud_reportes() 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',
        nullif(p_datos->>'rif_vence','')::date)) into v;
      perform fiscal.anotar_panel(v_quien, p_desde, 'cliente_guardado',
        jsonb_build_object('rif', p_datos->>'rif'));
    when 'activar_cliente' then
      select to_jsonb(fiscal.activar_cliente(p_datos->>'rif', (p_datos->>'activo')::boolean)) into v;
      perform fiscal.anotar_panel(v_quien, p_desde,
        case when (p_datos->>'activo')::boolean then 'cliente_activado' else 'cliente_suspendido' end,
        jsonb_build_object('rif', p_datos->>'rif'));
    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', p_datos->>'nombre')) into v;
      perform fiscal.anotar_panel(v_quien, p_desde, 'llave_creada',
        jsonb_build_object('rif', p_datos->>'rif', 'nombre', p_datos->>'nombre'));
    when 'revocar_llave' then
      select to_jsonb(fiscal.revocar_llave((p_datos->>'llave_id')::uuid)) into v;
      perform fiscal.anotar_panel(v_quien, p_desde, 'llave_revocada',
        jsonb_build_object('llave_id', p_datos->>'llave_id'));
    -- La pantalla llama 'relacion_mensual'/'resumen_mensual' y el despachador
    -- solo conocia 'relacion'/'resumen': la pestaña del reporte respondia
    -- "Accion desconocida" en produccion. Se aceptan los dos nombres.
    when 'relacion', '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 'resumen', '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 'marcar_enviado' then
      select to_jsonb(fiscal.marcar_enviado(
        p_datos->>'rif', (p_datos->>'anio')::int, (p_datos->>'mes')::int,
        v_quien, p_datos->>'nota')) into v;
      perform fiscal.anotar_panel(v_quien, p_desde, 'periodo_marcado_enviado',
        jsonb_build_object('rif', p_datos->>'rif', 'anio', p_datos->>'anio', 'mes', p_datos->>'mes'));
    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 to_jsonb(fiscal.vincular_negocio(
        (p_datos->>'restaurante_id')::uuid, p_datos->>'rif',
        coalesce(p_datos->>'serie', 'A'), v_quien)) into v;
      perform fiscal.anotar_panel(v_quien, p_desde, 'negocio_vinculado',
        jsonb_build_object('restaurante_id', p_datos->>'restaurante_id', 'rif', p_datos->>'rif'));
    when 'desvincular_negocio' then
      select to_jsonb(fiscal.desvincular_negocio((p_datos->>'restaurante_id')::uuid)) into v;
      perform fiscal.anotar_panel(v_quien, p_desde, 'negocio_desvinculado',
        jsonb_build_object('restaurante_id', p_datos->>'restaurante_id'));
    when 'negocios' then
      select coalesce(jsonb_agg(to_jsonb(t)), '[]'::jsonb) into v from fiscal.negocios_gustito() t;
    when 'estaciones' then
      select coalesce(jsonb_agg(to_jsonb(t)), '[]'::jsonb) into v from fiscal.estaciones_vivas() t;
    when 'cola' then
      select coalesce(jsonb_agg(to_jsonb(t)), '[]'::jsonb) into v from fiscal.cola_por_revisar() t;
    when 'movimientos' then
      select coalesce(jsonb_agg(to_jsonb(t)), '[]'::jsonb) into v
        from fiscal.movimientos_panel(coalesce((p_datos->>'limite')::int, 100)) t;
    when 'operadores' then
      select coalesce(jsonb_agg(to_jsonb(t)), '[]'::jsonb) into v from fiscal.operadores_lista() t;
    when 'guardar_operador' then
      select to_jsonb(fiscal.guardar_operador(
        p_datos->>'usuario', p_datos->>'nombre', p_datos->>'clave')) into v;
      perform fiscal.anotar_panel(v_quien, p_desde, 'operador_guardado',
        jsonb_build_object('usuario', lower(btrim(p_datos->>'usuario'))));
    when 'activar_operador' then
      select to_jsonb(fiscal.activar_operador(
        p_datos->>'usuario', (p_datos->>'activo')::boolean, p_yo)) into v;
      perform fiscal.anotar_panel(v_quien, p_desde,
        case when (p_datos->>'activo')::boolean then 'operador_activado' else 'operador_apagado' end,
        jsonb_build_object('usuario', lower(btrim(p_datos->>'usuario'))));
    else
      raise exception 'Accion desconocida';
  end case;
  return coalesce(v, 'null'::jsonb);
end;
$function$;

notify pgrst, 'reload schema';
