-- HERANCA DE CONTEXT SERVERS: do tenant para os projetos (Etapa 9, onda C).
--
-- O CENARIO QUE OBRIGA A ISSO
--
-- Um ERP multi-tenant tem uma operacao propria (o projeto "Geral") que consome
-- varios MCPs de contexto -- o do proprio ERP, o do eSocial, o da NFe -- e tem
-- N clientes finais, um projeto cada, que consomem SO o contexto do ERP.
-- Com context_servers preso a project_id NOT NULL, o MCP do ERP teria de ser
-- cadastrado N vezes, e a CREDENCIAL dele repetida N vezes: N copias do mesmo
-- segredo para rotacionar juntas, N linhas para corrigir quando o endpoint
-- muda. E exatamente o problema que a heranca semantica (0019) resolveu para a
-- interpretacao, agora para a CONEXAO.
--
-- A camada de cima e, como em 0019, a AUSENCIA de projeto:
--
--   project_id = 7      o servidor e do projeto 7;
--   project_id IS NULL  o servidor e do TENANT, herdado por todos os projetos
--                       daquele tenant.
--
-- tenant_id entra NOT NULL pelo mesmo motivo de 0019: e a barreira. A leitura
-- com heranca pergunta "os servidores deste projeto MAIS os do tenant dele", e
-- sem a coluna a segunda metade nao teria como ser escopada sem um JOIN com
-- projects em toda consulta -- com o risco de alguem escrever a versao sem o
-- JOIN e vazar a credencial de um tenant para outro. Com tenant_id na propria
-- linha, o filtro de tenant e a PRIMEIRA condicao de todo WHERE.
--
-- A credencial continua cifrada em authentication_config, e agora e cifrada UMA
-- vez: o servidor do tenant tem um unico blob, usado por todos os projetos.
--
--
-- UNICIDADE COM project_id NULL -- A DECISAO E O PORQUE
--
-- A UNIQUE antiga era (project_id, name). Trocada ingenuamente por
-- (tenant_id, project_id, name) ela NAO protegeria a camada nova: no
-- MySQL/InnoDB um indice UNIQUE aceita qualquer numero de linhas com NULL na
-- coluna indexada (NULL nao e igual a NULL), entao N servidores do tenant
-- chamados "erp" passariam pelo banco sem erro -- e "erp" e justamente o nome
-- que o agente usa para escolher o servidor. A camada que mais precisa da
-- garantia seria a unica sem garantia nenhuma.
--
-- Diferente de semantic_definitions -- onde a tabela GUARDA versoes repetidas
-- de proposito e por isso nunca teve UNIQUE --, context_servers tem no maximo
-- UMA linha por (camada, nome): nao ha historico, nao ha versao. Da para
-- fechar isso no banco, e nao apenas na aplicacao.
--
-- Por isso a chave e sobre uma coluna GERADA que troca NULL por 0:
--
--   scope_project_id = COALESCE(project_id, 0)   (VIRTUAL, nao ocupa espaco)
--   UNIQUE (tenant_id, scope_project_id, name)
--
-- Assim as duas camadas ficam protegidas pela MESMA chave: dois servidores
-- "erp" no projeto 7 batem, e dois servidores "erp" no nivel do tenant batem
-- tambem (ambos com scope_project_id = 0). Projeto e tenant continuam podendo
-- ter cada um o seu "erp" -- isso nao e colisao, e SOBRESCRITA (abaixo).
--
-- A verificacao na aplicacao (ContextService::add) continua existindo, mas com
-- outro papel: transformar a colisao em ContextServerAlreadyExistsException com
-- a camada e o nome na mensagem, em vez de deixar vazar um erro de constraint
-- do driver. O banco e a garantia; a aplicacao e a mensagem.
--
-- O indice antigo por project_id volta como KEY simples porque a FK para
-- projects precisa de um indice com project_id a esquerda, e a UNIQUE nova nao
-- serve para isso (project_id nem aparece nela).
--
--
-- PRECEDENCIA DE NOME (implementada em ContextService/MySQLContextServerRepository)
--
-- Se o projeto tem um servidor com o MESMO name de um herdado, o do projeto
-- VENCE e o herdado nao e listado ao lado dele -- mesma regra da semantica. E o
-- caminho para "este cliente fala com uma instancia propria do ERP": cadastre
-- "erp" no projeto e ele passa a responder por "erp" ali, sem tocar no do
-- tenant, que segue valendo para os demais projetos.
--
--
-- OPT-OUT: DESLIGAR UM HERDADO SEM APAGAR PARA OS OUTROS
--
-- enabled em context_servers e uma coluna da LINHA: desligar o servidor do
-- tenant desligaria para todos. Um cliente que nao deve consumir o MCP do ERP
-- precisaria, sem uma saida, de uma copia do servidor so para desliga-la --
-- exatamente a duplicacao que esta migration existe para eliminar.
--
-- context_server_optouts e essa saida, e por isso e uma TABELA DE LIGACAO e nao
-- uma coluna: quem desliga e o par (projeto, servidor), nao o servidor. A
-- ausencia de linha e o estado normal (herdado e ativo por padrao, que e o
-- ponto da heranca); a presenca da linha e a excecao explicita, com data.
--
-- So faz sentido para servidor DO TENANT. Para servidor do proprio projeto a
-- ferramenta continua sendo enabled (context:enable/disable) -- a aplicacao
-- recusa opt-out de servidor de projeto em vez de aceitar as duas formas de
-- dizer a mesma coisa.

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

-- Toda linha existente e de projeto: o tenant sai do proprio projeto.
UPDATE context_servers cs
  INNER JOIN projects p ON p.id = cs.project_id
   SET cs.tenant_id = p.tenant_id;

ALTER TABLE context_servers
  MODIFY COLUMN tenant_id BIGINT UNSIGNED NOT NULL,
  MODIFY COLUMN project_id BIGINT UNSIGNED NULL;

-- Antes de derrubar a UNIQUE antiga: a FK para projects precisa de um indice
-- que comece por project_id, e a UNIQUE nova nao tem project_id nenhum.
ALTER TABLE context_servers
  ADD KEY idx_context_servers_project (project_id);

ALTER TABLE context_servers
  DROP INDEX uq_context_servers_project_name;

ALTER TABLE context_servers
  ADD COLUMN scope_project_id BIGINT UNSIGNED
    GENERATED ALWAYS AS (COALESCE(project_id, 0)) VIRTUAL AFTER project_id;

ALTER TABLE context_servers
  ADD UNIQUE KEY uq_context_servers_scope_name (tenant_id, scope_project_id, name);

-- Serve a consulta nova: "os habilitados que este projeto enxerga", que filtra
-- tenant + camada + enabled e ordena por prioridade.
ALTER TABLE context_servers
  ADD KEY idx_context_servers_scope (tenant_id, project_id, enabled, priority),
  ADD CONSTRAINT fk_context_servers_tenant FOREIGN KEY (tenant_id) REFERENCES tenants (id);

CREATE TABLE context_server_optouts (
  id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
  project_id BIGINT UNSIGNED NOT NULL,
  context_server_id BIGINT UNSIGNED NOT NULL,
  created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
  UNIQUE KEY uq_context_server_optouts (project_id, context_server_id),
  KEY idx_context_server_optouts_server (context_server_id),
  CONSTRAINT fk_context_server_optouts_project FOREIGN KEY (project_id) REFERENCES projects (id),
  CONSTRAINT fk_context_server_optouts_server FOREIGN KEY (context_server_id) REFERENCES context_servers (id) ON DELETE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
