-- ===========================================================================
-- ESCOPO DA CHAVE: 'project' (como sempre) e 'tenant' (o admin geral)
-- ===========================================================================
--
-- O modelo "um projeto por cliente final" resolve o isolamento por construcao:
-- toda tabela filtra project_id, entao nenhuma consulta atravessa, nem por bug
-- nem por injecao de prompt. O que ele nao cobre e o ADMIN GERAL do ERP, que
-- precisa de UMA pergunta atravessando todos os clientes daquele tenant
-- ("quais clientes tem mais titulos vencidos?").
--
-- A resposta e a propria CREDENCIAL, nunca o payload: a chave nasce com um
-- escopo e ele nao muda em runtime. Uma chave de tenant amplia o alcance
-- DENTRO de um tenant e jamais o atravessa.
--
-- ---------------------------------------------------------------------------
-- POR QUE project_id PASSA A ACEITAR NULL
-- ---------------------------------------------------------------------------
-- Uma chave de tenant nao pertence a projeto nenhum: ela vale para todos os
-- projetos daquele tenant, inclusive os que ainda nao existem. Apontar para um
-- projeto "representante" seria mentira estrutural -- o dia em que esse projeto
-- fosse desativado, a chave do admin morreria junto, e qualquer consulta que
-- lesse project_id sem olhar o escopo silenciosamente filtraria pelo projeto
-- errado. NULL diz a verdade: "esta chave nao e de um projeto".
--
-- tenant_id, ao contrario, e SEMPRE preenchido -- nas duas modalidades. Nas
-- linhas que ja existem ele e derivado do projeto (UPDATE ... JOIN projects
-- abaixo), e a partir daqui e ele o primeiro filtro de qualquer consulta: o
-- teto que a chave nao ultrapassa. A CHECK garante estruturalmente o par
-- valido (scope='project' <-> project_id preenchido), de modo que uma chave de
-- projeto nao vire chave de tenant por UPDATE errado nem por bug de aplicacao.
-- ===========================================================================

ALTER TABLE api_keys
  ADD COLUMN tenant_id BIGINT UNSIGNED NULL AFTER id,
  ADD COLUMN scope VARCHAR(16) NOT NULL DEFAULT 'project' AFTER project_id;

-- Backfill: toda chave existente e de projeto, e o tenant dela e o do projeto.
UPDATE api_keys k
  INNER JOIN projects p ON p.id = k.project_id
  SET k.tenant_id = p.tenant_id;

ALTER TABLE api_keys
  MODIFY COLUMN tenant_id BIGINT UNSIGNED NOT NULL,
  MODIFY COLUMN project_id BIGINT UNSIGNED NULL,
  ADD KEY idx_api_keys_tenant (tenant_id, scope),
  ADD CONSTRAINT fk_api_keys_tenant FOREIGN KEY (tenant_id) REFERENCES tenants (id),
  ADD CONSTRAINT ck_api_keys_scope CHECK (
    (scope = 'project' AND project_id IS NOT NULL)
    OR (scope = 'tenant' AND project_id IS NULL)
  );

-- ===========================================================================
-- AUDITORIA: qual foi o ALCANCE da consulta, nao so quem a fez
-- ===========================================================================
--
-- Com chave de projeto a pergunta "que projeto foi tocado?" tinha resposta
-- unica e query_audit.project_id bastava. Com a consulta consolidada nao basta
-- mais: um UNION entre as views de dois clientes toca dois projetos, e gravar
-- um deles seria pior que nao gravar nenhum -- a trilha diria que a consulta
-- ficou dentro de um cliente quando ela atravessou dois.
--
-- Escolha (deliberada, entre as tres possiveis):
--
--   1. gravar uma linha por projeto tocado  -> multiplica o volume e mente
--      sobre o numero de execucoes (uma consulta, N linhas de "execucao");
--   2. gravar a lista de projetos num campo texto -> nao e consultavel por
--      indice e vira lixo de parsing na primeira pergunta seria;
--   3. (adotada) tenant_id SEMPRE preenchido; project_id preenchido quando a
--      consulta tocou EXATAMENTE um projeto e NULL quando tocou varios.
--
-- Com (3) a leitura da trilha e direta e sem ambiguidade:
--   project_id IS NOT NULL -> consulta contida naquele projeto;
--   project_id IS NULL     -> consulta consolidada, alcance = o tenant inteiro.
-- O tenant_id nunca e NULL, entao "de quem eram os dados" tem resposta em toda
-- linha -- que e a garantia que importa quando a chave e ampla.
--
-- Quem decide o project_id e o motor: a allowlist de views da consulta
-- consolidada sabe, view a view, de que projeto cada uma e, e QueryService
-- resolve o alcance a partir das tabelas que a instrucao referenciou.

ALTER TABLE query_audit
  ADD COLUMN tenant_id BIGINT UNSIGNED NULL AFTER id;

UPDATE query_audit a
  INNER JOIN projects p ON p.id = a.project_id
  SET a.tenant_id = p.tenant_id;

ALTER TABLE query_audit
  MODIFY COLUMN tenant_id BIGINT UNSIGNED NOT NULL,
  MODIFY COLUMN project_id BIGINT UNSIGNED NULL,
  ADD KEY idx_query_audit_tenant_created (tenant_id, created_at),
  ADD CONSTRAINT fk_query_audit_tenant FOREIGN KEY (tenant_id) REFERENCES tenants (id);
