-- 0037 — grupos de empresas: o alcance que faltava entre "um projeto" e "todos"
--
-- O BURACO
--
-- Ate aqui uma credencial alcancava UM projeto (chave de projeto) ou TODOS
-- (chave de tenant). Nao havia como dizer "estas onze empresas" -- e e o pedido
-- obvio numa contabilidade: o dono de uma holding tem cinco empresas e nao pode
-- ver as outras vinte e oito; um analista responde por uma carteira.
--
-- Sem isso, a unica saida era uma chave de projeto por empresa, e o usuario
-- trocaria de credencial para trocar de empresa -- ou receberia a chave de
-- tenant, que alcanca tudo. Os dois desfechos sao ruins pelo mesmo motivo: o
-- alcance para de descrever a pessoa.
--
--
-- OS DOIS PAPEIS, E POR QUE SO UM DELES E RESTRINGIVEL
--
--   Tenant ADMIN  alcanca tudo. E administracao: quem a recebe ja pode trocar o
--                 modelo e a chave que paga a conta. Restringir o alcance dele
--                 seria teatro.
--   Tenant USER   e o usuario PRINCIPAL do produto, e e ele que se prende a um
--                 grupo. Sem grupo vinculado, alcanca todos os projetos do
--                 tenant -- que e exatamente o comportamento de hoje, e por isso
--                 nada do que esta no ar muda.
--
--
-- POR QUE O GRUPO E CADASTRO, E NAO UMA LISTA DENTRO DA CHAVE
--
-- A alternativa seria `api_key_projects`: a chave carrega os ids. Funciona, e
-- quebra na operacao real -- empresa nova entra no grupo, e ai seria preciso
-- editar TODA chave daquele grupo, uma a uma. Alguem esqueceria uma, e o
-- sintoma seria um usuario que "nao ve a empresa nova" sem ninguem entender por
-- que.
--
-- Com o grupo como cadastro, "a empresa X e do grupo Y" e uma chamada de API no
-- provisionamento -- o mesmo lugar onde a empresa ja nasce --, e toda chave
-- apontada para Y passa a alcanca-la no mesmo instante.
--
--
-- MUITOS-PARA-MUITOS, ainda que o uso de hoje seja um
--
-- A tabela de ligacao custa o mesmo que uma FK e cobre "esta empresa e da
-- Holding X E da carteira do analista Z", que numa contabilidade aparece cedo.
-- O caminho contrario -- comecar com FK e migrar depois -- exigiria reescrever
-- toda leitura de alcance, que e o codigo mais sensivel do sistema.
--
--
-- O QUE ESTE GRUPO NAO E
--
-- Nao e `dataset_groups` (os assuntos: Financeiro, Cadastros). Aquilo classifica
-- DADO; isto delimita ALCANCE. Nomes parecidos, eixos sem relacao.
--
-- Nao e pacote (0036). Pacote e direito a CONTEUDO -- quem contratou o suporte.
-- Grupo e alcance de DADO. Juntar os dois faria alguem liberar o financeiro de
-- outra empresa ao conceder um pacote de suporte.

CREATE TABLE project_groups (
  id          BIGINT UNSIGNED NOT NULL AUTO_INCREMENT,
  tenant_id   BIGINT UNSIGNED NOT NULL,

  -- Como se refere a ele na API e na CLI.
  slug        VARCHAR(64)  NOT NULL,
  name        VARCHAR(120) NOT NULL,
  description VARCHAR(255) NULL,

  created_at  DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
  updated_at  DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,

  PRIMARY KEY (id),
  UNIQUE KEY uq_project_groups_slug (tenant_id, slug),
  CONSTRAINT fk_project_groups_tenant FOREIGN KEY (tenant_id) REFERENCES tenants(id)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;

CREATE TABLE project_group_members (
  id         BIGINT UNSIGNED NOT NULL AUTO_INCREMENT,
  group_id   BIGINT UNSIGNED NOT NULL,
  project_id BIGINT UNSIGNED NOT NULL,
  created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,

  PRIMARY KEY (id),
  UNIQUE KEY uq_project_group_members (group_id, project_id),
  KEY idx_project_group_members_project (project_id),

  -- CASCADE no grupo: apagado o grupo, a associacao nao significa mais nada.
  -- O PROJETO nao tem cascade: apagar projeto e operacao que o sistema nao faz.
  CONSTRAINT fk_pgm_group   FOREIGN KEY (group_id)   REFERENCES project_groups(id) ON DELETE CASCADE,
  CONSTRAINT fk_pgm_project FOREIGN KEY (project_id) REFERENCES projects(id)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;

-- ---------------------------------------------------------------------------
-- O VINCULO NA CREDENCIAL
-- ---------------------------------------------------------------------------
-- NULL = alcanca todos os projetos do tenant. E o comportamento de hoje, e por
-- isso toda chave existente continua identica.
--
-- SET NULL ao apagar o grupo, e nao CASCADE: uma chave nao pode DESAPARECER
-- porque alguem apagou um cadastro -- o widget do cliente pararia de abrir sem
-- explicacao. Ela volta a alcancar tudo, o que e visivel e corrigivel.
--
-- Vale so para chave de escopo TENANT e papel USER. A aplicacao recusa o
-- vinculo nos demais casos, em vez de guardar um estado que nenhuma leitura
-- aplicaria.
ALTER TABLE api_keys
  ADD COLUMN group_id BIGINT UNSIGNED NULL AFTER project_id,
  ADD KEY idx_api_keys_group (group_id),
  ADD CONSTRAINT fk_api_keys_group FOREIGN KEY (group_id)
    REFERENCES project_groups(id) ON DELETE SET NULL;

-- A sessao do widget CARREGA o grupo, em vez de reler pela chave.
--
-- A sessao e efemera e a chave e permanente: se o alcance fosse resolvido pela
-- chave a cada pergunta, mudar o grupo mudaria o alcance de uma conversa JA
-- ABERTA -- no meio dela, sem ninguem perceber. Congelado na emissao, o alcance
-- de uma sessao e o que ele era quando ela nasceu.
ALTER TABLE embed_sessions
  ADD COLUMN group_id BIGINT UNSIGNED NULL AFTER project_id,
  ADD KEY idx_embed_sessions_group (group_id),
  ADD CONSTRAINT fk_embed_sessions_group FOREIGN KEY (group_id)
    REFERENCES project_groups(id) ON DELETE SET NULL;
