#!/bin/sh
# ---------------------------------------------------------------------------
# Quem chama as tres exportacoes de vendas por hash -- SO CONTAGENS, por dia e
# por caminho, lidas do log de acesso do nginx (D4, D9).
#
#     sh scripts/medir-chamadas-unidade-vendas.sh /var/log/nginx/*.log /var/log/nginx/*.log.*
#     zcat /var/log/nginx/<um>.log.*.gz | sh scripts/medir-chamadas-unidade-vendas.sh
#
# Le os arquivos dados (o `.gz` tambem, pelo nome) ou, sem argumento, a entrada
# padrao. So le: nao escreve arquivo, nao toca banco, nao faz pedido. Sai com 0
# quando mediu; com 2 quando nao conseguiu medir -- arquivo que nao leu, nenhuma
# linha no formato, ou o controle em zero (abaixo). Nao e portao: o numero e
# para o dono.
#
# POR QUE ISTO EXISTE (08/10/2026). As tres rotas `/api/unidade/vendas`,
# `vendasV2` e `vendasV3` (`CronJobController::unidadeVendas*`, rotas Literal do
# `module/ISApi/config/module.config.php`) entregam o faturamento de uma unidade
# a quem apresenta o `hash` dela, e recusam com `die("Acesso negado.")` e 200. O
# dono decidiu nao mexer nelas "ate o inventario do D9 dizer quem as chama"
# (pergunta 6 do lote, 06/10/2026) -- e esse inventario, o das consultas do
# Power BI, morreu com ele em 09/09/2026. So o log de acesso da producao sabe.
# Na pergunta 33 da pagina do lote (07/10/2026) o dono escolheu a opcao (a):
# "Eu escrevo a conta, para rodar no dia 0" -- "So leitura e so contagens, por
# rota e por dia, sem hash nem IP; a sua 6 volta a voce com o numero". O passo
# esta no `docs/procedimento-de-corte.md`, em "O que hoje bloqueia".
#
# O QUE ELE IMPRIME: por arquivo, quantas linhas leu e quantas estao no formato
# `combined`; por dia, quantas linhas do site, quantos pedidos ao controle e a
# cada uma das tres rotas, e os "parecidos". NAO IMPRIME IP, `hash`, query
# string, user-agent, referer, metodo nem codigo de resposta: a query string das
# tres carrega o `hash`, que e a credencial da unidade, e o IP e dado pessoal.
# O caminho e cortado no `?` antes de qualquer conta, e so o dia sai do horario.
#
# O CONTROLE, e por que ele manda (o D36 e o D332). O site tem log proprio por
# vhost, e a coleta que leu so o `access.log*` achou ZERO pedidos a uma rota que
# o aplicativo chama a cada abertura. Zero nas tres, sozinho, nao separa
# "ninguem chamou" de "este nao e o log do site". Por isso a mesma leitura conta
# `/api/versao/app` (11.032 pedidos em 14 dias, no D36): com o controle em zero,
# o roteiro sai com 2 e diz que nao mediu; o dia com o controle em zero sai
# marcado: o zero dele nao quer dizer nada (o pedido que apareceu, sim, conta).
#
# AS TRES CONTAM TAMBEM NAS OUTRAS DUAS FORMAS que chegam a elas: o
# `/index.php/` na frente e a URL inteira no pedido (`GET http://<host>/...`).
# As duas dao 200 no nginx daqui, como o caminho curto (medido em 08/10/2026);
# o controle tambem as conta.
#
# "PARECIDOS" sao os caminhos que contem `/api/unidade/vendas` (sem caixa) e nao
# sao nenhuma das tres, em nenhuma das formas: a barra no fim, a barra dobrada,
# outra caixa, a `vendasV4` -- 404 no nginx daqui. Contados, e nao impressos: o
# caminho que ninguem previu pode carregar o que nao se imprime.
#
# O QUE ELE NAO DIZ. Quem e o chamador: a contagem por dia mostra o ritmo (todo
# dia na mesma quantidade parece integracao agendada; um dia so, alguem a mao ou
# um robo), e mais que isso pede o IP ou o user-agent, que este roteiro nao le de
# proposito. Se o `hash` estava certo: o 200 do `die("Acesso negado.")` e o 200
# da exportacao tem o mesmo codigo. E o que o log nao guarda: a janela e a dos
# dias que aparecem na saida (14, pela apuracao do dia 0), e "nao apareceu na
# janela" nao e "ninguem chama" -- uma exportacao mensal escapa de um log de
# duas semanas.
#
# O FORMATO e o `combined` do nginx:
#   $remote_addr - $remote_user [$time_local] "$request" $status $body_bytes_sent "$http_referer" "$http_user_agent"
# Do pedido, so o segundo campo (o caminho); do `[$time_local]`, so a data.
#
# Ensaio no repositorio, com um log INVENTADO: `sh scripts/ensaiar-medir-chamadas-unidade-vendas.sh`.
# ---------------------------------------------------------------------------

set -u

if [ "$#" -eq 0 ] && [ -t 0 ]; then
    echo "NAO CONSEGUI MEDIR: sem arquivo e sem entrada padrao. Passe os logs, ex.: sh $0 /var/log/nginx/*.log /var/log/nginx/*.log.*" >&2
    exit 2
fi

# Antes da leitura: um arquivo que nao se le faz a conta sair menor, calada.
for f in "$@"; do
    if [ ! -f "$f" ] || [ ! -r "$f" ]; then
        echo "NAO CONSEGUI MEDIR: nao consigo ler $f (o glob sem arquivo fica literal; rode como root)" >&2
        exit 2
    fi
done

# Cada arquivo entra precedido de uma linha de marca, que comeca por um byte de
# controle que o nginx nunca escreve no comeco da linha (ele a comeca pelo IP).
ler() {
    if [ "$#" -eq 0 ]; then
        printf '\001ARQUIVO (entrada padrao)\n'
        cat
        return
    fi
    for f in "$@"; do
        printf '\001ARQUIVO %s\n' "$f"
        case $f in
            *.gz) gzip -dc -- "$f" || printf '\001FALHOU %s\n' "$f" ;;
            *) cat -- "$f" || printf '\001FALHOU %s\n' "$f" ;;
        esac
    done
}

ler "$@" | awk '
BEGIN {
    split("Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec", nomes, " ")
    for (i = 1; i <= 12; i++) mes[nomes[i]] = sprintf("%02d", i)
    rota[1] = "/api/unidade/vendas"
    rota[2] = "/api/unidade/vendasV2"
    rota[3] = "/api/unidade/vendasV3"
    CONTROLE = "/api/versao/app"
    narq = 0; ndias = 0; falhou = 0
}

index($0, "\001ARQUIVO ") == 1 { arquivo = substr($0, 10); ordem[++narq] = arquivo; next }
index($0, "\001FALHOU ") == 1 { falhou = 1; next }

{
    linhas[arquivo]++

    # O dia: o primeiro "[dd/Mmm/aaaa:" da linha.
    a = index($0, " [")
    if (a == 0) next
    resto = substr($0, a + 2)
    if (substr(resto, 3, 1) != "/" || substr(resto, 7, 1) != "/" || substr(resto, 12, 1) != ":") next
    d = substr(resto, 1, 2); mm = substr(resto, 4, 3); aaaa = substr(resto, 8, 4)
    if (!(mm in mes) || d !~ /^[0-3][0-9]$/ || aaaa !~ /^[12][0-9][0-9][0-9]$/) next
    dia = aaaa "-" mes[mm] "-" d

    # O pedido: entre as aspas logo depois do "] ". So o segundo campo dele.
    b = index(resto, "] \"")
    if (b == 0) next
    pedido = substr(resto, b + 3)
    c = index(pedido, "\"")
    if (c == 0) next
    pedido = substr(pedido, 1, c - 1)
    if (split(pedido, partes, " ") < 2) next
    caminho = partes[2]
    q = index(caminho, "?"); if (q > 0) caminho = substr(caminho, 1, q - 1)
    h = index(caminho, "#"); if (h > 0) caminho = substr(caminho, 1, h - 1)
    # AS DUAS OUTRAS FORMAS QUE CHEGAM A EXPORTACAO (a revisao de 08/10/2026):
    # o pedido com a URL inteira ("GET http://<host>/api/unidade/vendas", a
    # forma absoluta, que o nginx aceita e grava assim) e o `/index.php/` na
    # frente, que o Laminas tira antes de rotear. Medido no nginx daqui (o
    # mesmo `docker/nginx/default.conf`), so com GET sem `id` e sem `hash`: as
    # duas dao 200 com o "Acesso negado." da action -- a exportacao rodou. Sem
    # tirar as duas, elas iam a "parecidos", e as tres saiam em zero com alguem
    # chamando. A barra no fim e a barra dobrada continuam 404: sao vizinhas.
    if (caminho ~ /^[A-Za-z][A-Za-z0-9+.-]*:\/\//) sub(/^[A-Za-z][A-Za-z0-9+.-]*:\/\/[^\/]*/, "", caminho)
    if (index(caminho, "/index.php/") == 1) caminho = substr(caminho, 11)

    noFormato[arquivo]++
    if (!(dia in lidas)) { dias[++ndias] = dia }
    lidas[dia]++
    if (caminho == CONTROLE) { controle[dia]++; totalControle++ }

    achou = 0
    for (i = 1; i <= 3; i++) {
        if (caminho == rota[i]) { pedidos[dia, i]++; total[i]++; dasTres[arquivo]++; achou = 1 }
    }
    if (!achou && index(tolower(caminho), "/api/unidade/vendas") > 0) {
        parecido[dia]++; totalParecido++; dasTres[arquivo]++
    }
}

END {
    # Os dias em ordem: aaaa-mm-dd ordena como texto.
    for (i = 2; i <= ndias; i++) {
        v = dias[i]; j = i - 1
        while (j >= 1 && dias[j] > v) { dias[j + 1] = dias[j]; j-- }
        dias[j + 1] = v
    }

    print "QUEM CHAMA AS TRES EXPORTACOES unidadeVendas* -- so contagens, por dia e por caminho"
    print "(sem IP, sem hash, sem query string, sem user-agent: so o dia e o caminho)"
    print ""
    print "ARQUIVOS LIDOS"
    printf "  %10s %10s %10s  %s\n", "linhas", "no formato", "das tres*", "arquivo"
    for (k = 1; k <= narq; k++) {
        f = ordem[k]
        printf "  %10d %10d %10d  %s\n", linhas[f], noFormato[f], dasTres[f], f
    }
    print "  (* as tres rotas e os parecidos)"
    print ""
    print "POR DIA"
    printf "  %-10s %9s %16s %9s %9s %9s %9s\n", "dia", "do site", "/api/versao/app", "vendas", "vendasV2", "vendasV3", "parecidos"
    for (k = 1; k <= ndias; k++) {
        x = dias[k]
        marca = (controle[x] + 0 == 0) ? "  <- o controle deu zero: o zero deste dia nao quer dizer nada" : ""
        printf "  %-10s %9d %16d %9d %9d %9d %9d%s\n", x, lidas[x], controle[x], pedidos[x, 1], pedidos[x, 2], pedidos[x, 3], parecido[x], marca
    }
    printf "  %-10s %9s %16d %9d %9d %9d %9d\n", "total", "", totalControle, total[1], total[2], total[3], totalParecido
    print ""

    if (falhou) {
        print "NAO CONSEGUI MEDIR: um dos arquivos nao foi lido ate o fim (o erro esta acima, na saida de erro)."
        exit 2
    }
    if (ndias == 0) {
        print "NAO CONSEGUI MEDIR: nenhuma linha no formato combined -- e o log de acesso do nginx?"
        exit 2
    }
    if (totalControle == 0) {
        print "NAO CONSEGUI MEDIR: o controle (/api/versao/app) deu zero. Estes nao sao os logs do site (o D36:"
        print "o site tem log proprio por vhost), e o zero das tres rotas nao quer dizer nada."
        exit 2
    }
    printf "JANELA: %d dia(s) com linha, de %s a %s.\n", ndias, dias[1], dias[ndias]
    printf "AS TRES: %d pedido(s) -- vendas %d, vendasV2 %d, vendasV3 %d -- e %d parecido(s).\n", total[1] + total[2] + total[3], total[1], total[2], total[3], totalParecido
    print "O numero volta ao dono (pergunta 6 do lote): zero na janela nao e \"ninguem chama\", e mais que zero nao diz quem."
    exit 0
}'
