-- ART. 29 NUMERAL 2 DE LA 000102: la imprenta debe solicitar al emisor el
-- comprobante digital del RIF VIGENTE. El RIF vence (tres años) y hasta hoy el
-- sistema solo guardaba un si/no de "comprobante recibido", sin fecha: un
-- comprobante vencido seguia valiendo para siempre. Ahora el alta guarda la
-- fecha de vencimiento que trae el comprobante, el panel la muestra y avisa,
-- y la numeracion se FRENA para el emisor con RIF vencido.
--
-- El freno es TRANSITORIO a proposito: el mensaje no dispara el marcado de
-- "rechazado" en la cola de la Estacion, asi que lo que quede en cola fluye
-- solo cuando la imprenta registre el comprobante renovado. Y frena SOLO la
-- asignacion de numeros: el latido y las consultas de la Estacion siguen
-- pasando, para que el negocio vea el aviso en vez de quedarse a ciegas.

alter table fiscal.emisores add column if not exists rif_vence date;

-- El alta gana la fecha de vencimiento. Cambia la firma: fuera la vieja para
-- no dejar un doble (overload) que confunda las llamadas posicionales.
drop function if exists fiscal.guardar_cliente(text, text, text, text, text, text, text, text, text, boolean, text);

create function fiscal.guardar_cliente(
  p_rif text, p_razon_social text, p_domicilio text,
  p_representante text default null, p_cedula text default null,
  p_correo text default null, p_telefono text default null,
  p_sistema text default null, p_serie text default 'A',
  p_rif_comprobado boolean default false, p_nota text default null,
  p_rif_vence date default null
) returns text
language plpgsql
security definer
set search_path to 'fiscal', 'public'
as $$
declare v_rif text;
begin
  if not public.fn_rif_valido(p_rif) then
    raise exception 'Ese RIF no es valido, revisa el ultimo digito';
  end if;
  v_rif := public.fn_rif_normalizado(p_rif);
  insert into fiscal.emisores (rif, razon_social, domicilio_fiscal, llave_hash,
                               representante, cedula_representante, correo, telefono,
                               sistema, serie_asignada, rif_comprobado, nota, rif_vence)
    values (v_rif, trim(p_razon_social), trim(p_domicilio), 'sin-uso',
            p_representante, p_cedula, p_correo, p_telefono,
            p_sistema, upper(coalesce(p_serie, 'A')), p_rif_comprobado, p_nota, p_rif_vence)
    on conflict (rif) do update set
      razon_social = excluded.razon_social,
      domicilio_fiscal = excluded.domicilio_fiscal,
      representante = excluded.representante,
      cedula_representante = excluded.cedula_representante,
      correo = excluded.correo,
      telefono = excluded.telefono,
      sistema = excluded.sistema,
      serie_asignada = excluded.serie_asignada,
      rif_comprobado = excluded.rif_comprobado,
      nota = excluded.nota,
      rif_vence = excluded.rif_vence;
  return v_rif;
end;
$$;

-- La lista de clientes muestra el vencimiento. Cambia el tipo de retorno:
-- fuera la vieja primero.
drop function if exists fiscal.clientes();

create function fiscal.clientes()
returns table(
  serial text, rif text, razon_social text, correo text, telefono text,
  sistema text, serie_asignada text, activo boolean, rif_comprobado boolean,
  rif_vence date, llaves_activas integer, documentos_mes integer,
  total_mes_bs numeric, ultimo_documento timestamptz, fallidos_hoy integer,
  respaldo_lineas integer, respaldo_ultimo timestamptz
)
language sql
stable
security definer
set search_path to 'fiscal', 'public'
as $$
  select e.serial, e.rif, e.razon_social, e.correo, e.telefono, e.sistema, e.serie_asignada,
         e.activo, e.rif_comprobado, e.rif_vence,
         (select count(*)::int from fiscal.llaves l where l.emisor_id = e.id and l.activa),
         (select count(*)::int from fiscal.documentos d
           where d.emisor_id = e.id and d.fecha_emision >= date_trunc('month', now())),
         coalesce((select round(sum(d.total_bs), 2) from fiscal.documentos d
           where d.emisor_id = e.id and d.fecha_emision >= date_trunc('month', now())), 0),
         (select max(d.recibido_en) from fiscal.documentos d where d.emisor_id = e.id),
         (select count(*)::int from fiscal.intentos i
           where i.tipo = 'estacion' and i.quien = e.rif and not i.entro and i.en >= current_date),
         (select count(*)::int from fiscal.respaldo r where r.emisor_id = e.id),
         (select max(r.recibido_en) from fiscal.respaldo r where r.emisor_id = e.id)
    from fiscal.emisores e
   order by e.razon_social;
$$;

-- La numeracion se frena con el RIF vencido. Mismo tipo de retorno que dejo la
-- migracion 20260731070000: CREATE OR REPLACE basta.
create or replace function fiscal.recibir_documento(
  p_rif text, p_serie text, p_tipo text, p_numero_documento bigint, p_hash text,
  p_fecha timestamptz, p_base_bs numeric, p_iva_bs numeric, p_total_bs numeric,
  p_documento jsonb
) returns table(
  numero_control bigint, token text, ya_estaba boolean,
  fecha_asignacion timestamptz, rango_desde bigint, rango_hasta bigint,
  imprenta_rif text, imprenta_razon_social text,
  imprenta_providencia text, imprenta_providencia_fecha date
)
language plpgsql
security definer
set search_path to 'fiscal', 'public', 'extensions'
as $$
declare
  v_emisor uuid; v_control bigint; v_token text; v_fecha timestamptz;
  v_desde bigint; v_hasta bigint;
  v_imp_rif text; v_imp_razon text; v_imp_prov text; v_imp_prov_fecha date;
  v_existia boolean := false;
begin
  select e.id into v_emisor from fiscal.emisores e
   where e.rif = public.fn_rif_normalizado(p_rif) and e.activo;
  if v_emisor is null then
    raise exception 'Ese RIF no esta autorizado a emitir por esta imprenta';
  end if;

  -- Art. 29 num. 2: no se asignan numeros de control a quien no acredita RIF
  -- vigente. El texto evita a proposito las palabras que la cola de la
  -- Estacion trata como rechazo definitivo: esto se resuelve consignando el
  -- comprobante renovado, y entonces lo encolado sale solo.
  if exists (select 1 from fiscal.emisores e
              where e.id = v_emisor and e.rif_vence is not null
                and e.rif_vence < current_date) then
    raise exception 'El comprobante de RIF de este emisor esta vencido. Consigne ante la imprenta el comprobante renovado para seguir emitiendo.';
  end if;

  -- Reintento del cliente: mismo hash, mismo numero. La fecha de asignacion es
  -- la ORIGINAL (cuando de verdad se asigno), no la del reintento.
  select d.numero_control, d.token, d.recibido_en into v_control, v_token, v_fecha
    from fiscal.documentos d
   where d.emisor_id = v_emisor and d.hash_documento = p_hash;
  if found then
    v_existia := true;
  else
    v_control := fiscal.asignar_control(v_emisor, p_serie);
    v_token := encode(extensions.gen_random_bytes(16), 'hex');
    v_fecha := now();
    insert into fiscal.documentos (
      emisor_id, serie, tipo, numero_documento, numero_control, token,
      hash_documento, fecha_emision, base_bs, iva_bs, total_bs, documento
    ) values (
      v_emisor, p_serie, p_tipo, p_numero_documento, v_control, v_token,
      p_hash, p_fecha, p_base_bs, p_iva_bs, p_total_bs, p_documento
    );
  end if;

  select r.desde, r.hasta into v_desde, v_hasta
    from fiscal.rangos_emisor r
   where r.emisor_id = v_emisor and not r.anulado
     and v_control between r.desde and r.hasta
   order by r.desde
   limit 1;

  select i.rif, i.razon_social, i.providencia, i.providencia_fecha
    into v_imp_rif, v_imp_razon, v_imp_prov, v_imp_prov_fecha
    from fiscal.imprenta i
   limit 1;

  return query select v_control, v_token, v_existia, v_fecha, v_desde, v_hasta,
                      v_imp_rif, v_imp_razon, v_imp_prov, v_imp_prov_fecha;
end;
$$;

-- El interruptor de Gustito conoce el motivo nuevo: rif_vencido.
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 $$
declare
  v_neg record;
  v_llaves int;
begin
  select n.serie, e.rif, e.razon_social, e.domicilio_fiscal, e.activo, e.rif_comprobado, e.rif_vence
    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;

  -- Y el comprobante tiene que estar VIGENTE (art. 29 num. 2).
  if v_neg.rif_vence is not null and v_neg.rif_vence < current_date then
    return jsonb_build_object('activa', false, 'motivo', 'rif_vencido');
  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;
$$;

-- El despachador del panel pasa la fecha nueva al alta (mismo retorno,
-- CREATE OR REPLACE basta; el resto de los casos quedan identicos a la
-- migracion 20260731080000).
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'));

    -- ===== 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';
