-- ---------------------------------------------------------------------------
-- Uma conclusao por pessoa e curso na VIP Class: as linhas a mais de cada par
-- (curso, pessoa) saem de `vipclass_checkpoints_usuarios`, FICA A PRIMEIRA, e o
-- indice unico (vipclass, usuario) entra (o D372; a F5 de
-- `docs/f3-telas-restantes/tela-vipclass.md`, a fatia 85 da fila das telas que faltam).
--
--   mysql franquiabv < scripts/migracao-vipclass-conclusao-unica.sql
--
-- RODA NO PASSO 3 DA JANELA, E NAO NO PASSO 0 (a resposta 192 (a) do dono: o passo 3
-- e a etapa 2). Ela APAGA LINHA -- a conclusao a mais de cada par --, e o laco do
-- passo 0 so leva o que acrescenta; a etapa 1 copia aquele laco. Aqui, depois da
-- limpeza e antes do dump, como as outras migracoes que apagam linha.
--
-- DECIDIDO PELO DONO EM 15/10/2026 (a pergunta 3 do desenho, a resposta 213 (a) da
-- pagina do lote): "Uma conclusao. Vale a primeira aprovacao, concluir de novo e
-- recusado, e as 191 linhas a mais saem no corte." O codigo ja recusa a segunda desde
-- 05/11/2026 (o envio rele a conclusao sob a trava da pessoa, as fatias 79 e 80), e as
-- telas ja mostram a primeira (o cartao, o "Quem fez" e os dois relatorios antigos);
-- daqui em diante e o banco que recusa.
--
-- QUAL LINHA FICA: a PRIMEIRA de cada par -- a de menor `data` e, se duas tem a mesma
-- `data` (a coluna e `datetime`, sem fracao de segundo), a de menor `id`. E a regra de
-- `RegraDoCursoVipClass::primeira` e de `VipClassCheckpointUsuarioRepository::
-- primeirasDoAluno`, que as telas ja leem. Sai toda linha que tem, no mesmo par, outra
-- mais antiga (ou do mesmo instante e de id menor) -- e por isso NENHUM PAR FICA SEM
-- LINHA: a primeira de cada par nao tem quem a preceda. Na copia de 16/08/2026 (so
-- contagens, 06/11/2026): 2.131 conclusoes de 1.940 pares; 167 pares com mais de uma,
-- de 119 pessoas em 17 cursos; 191 linhas saem, todas com a data diferente da primeira,
-- e 4 com a nota diferente (em 3, a que sai tem nota MAIOR que a primeira -- a regra e a
-- primeira aprovacao, e nao a maior nota); nenhum empate de data na primeira; em todos
-- os pares a primeira e tambem a de menor id.
--
-- NADA SOME DE QUEM CONCLUIU: cada pessoa continua com uma conclusao em cada curso que
-- concluiu, a primeira, com a data e a nota que a tela dela e o "Quem fez" ja mostram. O
-- certificado nao imprime data nem nota (`PdfVipClassCertificado`), e sai igual. O que
-- sai sao as tentativas repetidas, que so voltam pelo dump de antes da janela.
--
-- IDEMPOTENTE: sem pares, o DELETE muda zero linhas; com a chave, o ALTER diz que ela ja
-- existe. Rodar de novo nao muda nada e nao falha.
--
-- NAO COPIA A TABELA: indice secundario o InnoDB cria INPLACE, com LOCK=NONE (2.131
-- linhas na copia). Se o servidor recusar o INPLACE, o ALTER falha e nao muda nada -- e
-- o DELETE de antes ja foi: rodar de novo nao o refaz (nada mais a tirar), e tenta o ALTER
-- outra vez.
--
-- E O CODIGO ANTIGO NUMA VOLTA: com o indice, o envio repetido do checkpoint antigo, que
-- gravava a segunda conclusao calado, passa a dar erro. O codigo novo rele sob a trava e,
-- se a corrida passar por ela, trata a recusa do banco como "voce ja concluiu"
-- (`VipClassEduService::checkpointFinalizar`).
--
-- AS COPIAS LOCAIS AINDA NAO A RECEBERAM: a resposta 213 (a) do dono ja autoriza a limpeza
-- nelas ("nas duas copias locais ja"), mas a fatia 85 nao a rodou (so SELECT na
-- copia local, pela ordem da fatia); rodar e do coordenador (o D728). O PostgreSQL da janela
-- nasce com a chave pelo gerador (`$unicidades` de `scripts/gerar-ddl-postgres.php`); o da
-- copia local tem o irmao em `postgres/`.
-- ---------------------------------------------------------------------------

SELECT '=== ANTES: pares (curso, pessoa), pares repetidos, linhas a mais, das que saem com outra nota e com outra data ===' AS '';
SELECT (SELECT COUNT(*) FROM (SELECT 1 FROM vipclass_checkpoints_usuarios GROUP BY vipclass, usuario) t),
       COUNT(*),
       COALESCE(SUM(n) - COUNT(*), 0),
       (SELECT COUNT(*) FROM vipclass_checkpoints_usuarios c
          JOIN vipclass_checkpoints_usuarios f ON f.vipclass = c.vipclass AND f.usuario = c.usuario
         WHERE c.id <> f.id AND c.nota <> f.nota
           AND NOT EXISTS (SELECT 1 FROM vipclass_checkpoints_usuarios p
                            WHERE p.vipclass = f.vipclass AND p.usuario = f.usuario
                              AND (p.data < f.data OR (p.data = f.data AND p.id < f.id)))),
       (SELECT COUNT(*) FROM vipclass_checkpoints_usuarios c
          JOIN vipclass_checkpoints_usuarios f ON f.vipclass = c.vipclass AND f.usuario = c.usuario
         WHERE c.id <> f.id AND c.data <> f.data
           AND NOT EXISTS (SELECT 1 FROM vipclass_checkpoints_usuarios p
                            WHERE p.vipclass = f.vipclass AND p.usuario = f.usuario
                              AND (p.data < f.data OR (p.data = f.data AND p.id < f.id))))
  FROM (SELECT COUNT(*) AS n FROM vipclass_checkpoints_usuarios
         GROUP BY vipclass, usuario HAVING COUNT(*) > 1) r;

-- O que o DELETE tem de tirar, guardado para a conferencia do fim.
SET @pares_antes = (SELECT COUNT(*) FROM (SELECT 1 FROM vipclass_checkpoints_usuarios GROUP BY vipclass, usuario) t);
SET @linhas_antes = (SELECT COUNT(*) FROM vipclass_checkpoints_usuarios);

-- 1. A LISTA DOS PARES: o curso, a pessoa (os ids), a conclusao que fica, as que saem e
--    os segundos entre a primeira e a ultima. Vazia quando nao ha par.
SELECT '=== OS PARES: curso, pessoa, a conclusao que fica, as que saem, segundos entre a primeira e a ultima ===' AS '';
SELECT f.vipclass, f.usuario, f.id,
       (SELECT GROUP_CONCAT(s.id ORDER BY s.id) FROM vipclass_checkpoints_usuarios s
         WHERE s.vipclass = f.vipclass AND s.usuario = f.usuario AND s.id <> f.id),
       (SELECT TIMESTAMPDIFF(SECOND, f.data, MAX(u.data)) FROM vipclass_checkpoints_usuarios u
         WHERE u.vipclass = f.vipclass AND u.usuario = f.usuario)
  FROM vipclass_checkpoints_usuarios f
 WHERE NOT EXISTS (SELECT 1 FROM vipclass_checkpoints_usuarios p
                    WHERE p.vipclass = f.vipclass AND p.usuario = f.usuario
                      AND (p.data < f.data OR (p.data = f.data AND p.id < f.id)))
   AND EXISTS (SELECT 1 FROM vipclass_checkpoints_usuarios o
                WHERE o.vipclass = f.vipclass AND o.usuario = f.usuario AND o.id <> f.id)
 ORDER BY f.vipclass, f.usuario;

-- 2. AS LINHAS A MAIS, numa transacao: sai toda conclusao que tem, no mesmo par, outra
--    mais antiga -- ou do mesmo instante e de id menor. A primeira de cada par fica.
START TRANSACTION;

DELETE c FROM vipclass_checkpoints_usuarios c
  JOIN vipclass_checkpoints_usuarios p
    ON p.vipclass = c.vipclass AND p.usuario = c.usuario
   AND (p.data < c.data OR (p.data = c.data AND p.id < c.id));

SET @apagadas = ROW_COUNT();

COMMIT;

SELECT '=== O QUE SAIU: linhas apagadas ===' AS '';
SELECT @apagadas;

-- 3. A CHAVE, com as linhas a mais ja tiradas. Na segunda vez, o aviso de que ela ja existe.
SELECT '=== A CHAVE: o aviso, se ela ja existe ===' AS '';

SET @existe = (
    SELECT COUNT(*) FROM information_schema.statistics
     WHERE table_schema = DATABASE()
       AND table_name = 'vipclass_checkpoints_usuarios'
       AND index_name = 'uq_vipclass_checkpoints_usuarios_vipclass_usuario'
);

SET @sql = IF(@existe = 0,
    'ALTER TABLE vipclass_checkpoints_usuarios
       ADD UNIQUE KEY uq_vipclass_checkpoints_usuarios_vipclass_usuario (vipclass, usuario),
       ALGORITHM=INPLACE, LOCK=NONE',
    'SELECT ''o indice das conclusoes ja existe, nada a fazer'' AS aviso'
);

PREPARE stmt FROM @sql;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;

-- A CONFERENCIA: nenhum par repetido; os pares sao os mesmos de antes (nenhum perdeu a
-- primeira); uma linha por par; e o que saiu e o que estava a mais.
SELECT '=== DEPOIS: pares repetidos (0), pares antes e depois (iguais), linhas (uma por par), apagadas e a mais de antes (iguais) ===' AS '';
SELECT (SELECT COUNT(*) FROM (SELECT 1 FROM vipclass_checkpoints_usuarios
                               GROUP BY vipclass, usuario HAVING COUNT(*) > 1) r),
       @pares_antes,
       (SELECT COUNT(*) FROM (SELECT 1 FROM vipclass_checkpoints_usuarios GROUP BY vipclass, usuario) t),
       (SELECT COUNT(*) FROM vipclass_checkpoints_usuarios),
       @apagadas,
       @linhas_antes - @pares_antes;

SELECT '=== INDICE: o nome, as colunas e se e unico (0) ===' AS '';
SELECT index_name, GROUP_CONCAT(column_name ORDER BY seq_in_index), MAX(non_unique)
  FROM information_schema.statistics
 WHERE table_schema = DATABASE()
   AND table_name = 'vipclass_checkpoints_usuarios'
   AND index_name = 'uq_vipclass_checkpoints_usuarios_vipclass_usuario'
 GROUP BY index_name;
