-- HERANCA DE SEMANTICA E DOCUMENTOS: do tenant para os projetos (Etapa 9, onda B).
--
-- POR QUE project_id VIROU NULLABLE
--
-- O modelo do cliente e "um projeto por cliente final": o isolamento e
-- estrutural, toda consulta filtra project_id e nenhuma atravessa. O que esse
-- modelo nao cobre e a REGRA DA CASA. O significado de STATUS_ID = 3 e a
-- politica de cobranca da empresa sao os mesmos para todos os clientes; sem
-- heranca eles teriam de ser cadastrados em CADA um dos N projetos e
-- recorrigidos N vezes a cada mudanca. "Um projeto por cliente" so e
-- administravel se existir uma camada acima do projeto.
--
-- Essa camada e a AUSENCIA de projeto:
--
--   project_id = 7      a linha e do projeto 7;
--   project_id IS NULL  a linha e do TENANT, herdada por todos os projetos
--                       daquele tenant.
--
-- Por isso project_id passa a aceitar NULL nas tres tabelas de interpretacao e
-- de conhecimento -- e por isso tenant_id entra como NOT NULL em todas elas.
-- tenant_id nao e conveniencia: e a barreira. A resolucao com heranca precisa
-- perguntar "as linhas deste projeto MAIS as linhas do tenant dele", e sem a
-- coluna essa segunda metade nao teria como ser escopada -- a alternativa seria
-- um JOIN com projects em toda leitura, com o risco de alguem escrever a versao
-- sem o JOIN e vazar a semantica de um tenant para outro. Com tenant_id na
-- propria linha, o filtro de tenant e a PRIMEIRA condicao de todo WHERE, do
-- mesmo jeito que project_id e hoje.
--
-- A REGRA DE SOBRESCRITA (implementada em SemanticService/SearchService):
-- procura-se primeiro no projeto; so se nao houver, cai para o tenant. A
-- definicao do projeto SEMPRE vence a do tenant para o mesmo assunto, e
-- corrigir a partir do projeto uma definicao HERDADA cria uma definicao DE
-- PROJETO -- a do tenant fica intacta e ativa, valendo para os demais.
--
-- CHAVE DE UNICIDADE, COM project_id NULL
--
-- semantic_definitions NUNCA teve UNIQUE: o que existe e a KEY
-- idx_semantic_definitions_subject (project_id, subject(191), active), um
-- indice de leitura. A unicidade da definicao ATIVA por (escopo, assunto)
-- sempre foi garantida pela aplicacao -- SELECT ... FOR UPDATE na linha ativa
-- dentro da transacao, mais a recontagem defensiva de SemanticService --,
-- justamente porque um UNIQUE em (project_id, subject, active) nao serviria:
-- ele impediria duas versoes INATIVAS do mesmo assunto, que sao exatamente o
-- historico que esta tabela existe para guardar.
--
-- Como a chave e KEY e nao UNIQUE, project_id NULL nao afrouxa restricao
-- nenhuma: nao havia restricao. Fica registrado, ainda assim, o efeito que
-- valeria se um dia alguem promovesse essa chave a UNIQUE: no MySQL/InnoDB um
-- indice UNIQUE aceita QUALQUER numero de linhas com NULL na coluna indexada
-- (NULL nao e igual a NULL), entao a linha do tenant simplesmente nao seria
-- protegida -- N definicoes ativas do mesmo assunto no nivel do tenant
-- passariam pelo banco sem erro. Um UNIQUE util aqui teria de ser sobre uma
-- coluna gerada que troque NULL por 0 (COALESCE(project_id, 0)). A garantia
-- continua sendo a transacao da aplicacao, e ela ja escopa por camada.
--
-- Os indices novos servem as consultas novas: (tenant_id, project_id, subject)
-- para a resolucao com heranca e (tenant_id, project_id, status) para a busca
-- em dois niveis. Os indices antigos por project_id continuam, porque a
-- consulta "so o que e deste projeto" continua existindo.

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

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

ALTER TABLE semantic_definitions
  MODIFY COLUMN tenant_id BIGINT UNSIGNED NOT NULL,
  MODIFY COLUMN project_id BIGINT UNSIGNED NULL,
  ADD KEY idx_semantic_definitions_scope (tenant_id, project_id, subject(191), active),
  ADD CONSTRAINT fk_semantic_definitions_tenant FOREIGN KEY (tenant_id) REFERENCES tenants (id);

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

UPDATE semantic_corrections c
  INNER JOIN projects p ON p.id = c.project_id
   SET c.tenant_id = p.tenant_id;

-- A correcao acompanha a camada da definicao que ela substituiu: corrigir no
-- nivel do tenant tem de deixar trilha no nivel do tenant, senao a autoria da
-- regra da casa nao teria onde ser gravada.
ALTER TABLE semantic_corrections
  MODIFY COLUMN tenant_id BIGINT UNSIGNED NOT NULL,
  MODIFY COLUMN project_id BIGINT UNSIGNED NULL,
  ADD KEY idx_semantic_corrections_scope (tenant_id, project_id, semantic_definition_id),
  ADD CONSTRAINT fk_semantic_corrections_tenant FOREIGN KEY (tenant_id) REFERENCES tenants (id);

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

UPDATE knowledge_items k
  INNER JOIN projects p ON p.id = k.project_id
   SET k.tenant_id = p.tenant_id;

ALTER TABLE knowledge_items
  MODIFY COLUMN tenant_id BIGINT UNSIGNED NOT NULL,
  MODIFY COLUMN project_id BIGINT UNSIGNED NULL,
  ADD KEY idx_knowledge_items_scope_status (tenant_id, project_id, status),
  ADD KEY idx_knowledge_items_scope_identifier (tenant_id, project_id, identifier(191)),
  ADD CONSTRAINT fk_knowledge_items_tenant FOREIGN KEY (tenant_id) REFERENCES tenants (id);

-- COMO UMA FONTE DE INDEXACAO VIRA "DO TENANT"
--
-- A fonte continua pertencendo a um projeto (project_id NOT NULL): e de la que
-- o operador a administra, e dali sai a proveniencia. O que a coluna decide e
-- ONDE OS ITENS GERADOS caem -- scope='tenant' faz cada knowledge_item nascer
-- com project_id NULL, visivel para todos os projetos do tenant.
--
-- A alternativa seria uma fonte "sem projeto" (project_id NULL tambem aqui),
-- que obrigaria a mexer na UNIQUE (project_id, name), na listagem, na CLI e nas
-- telas do painel -- tudo isso para nao guardar informacao nenhuma a mais. Uma
-- coluna de escopo diz exatamente o que muda e nao mexe em nada que ja funciona.
ALTER TABLE index_sources
  ADD COLUMN scope VARCHAR(16) NOT NULL DEFAULT 'project' AFTER project_id;
