"""planejamento como contexto: um schema do Postgres por Planejamento

Revision ID: e8c1b7a45d93
Revises: d3f6b8a1c294
Create Date: 2026-10-29 12:00:00.000000

O registro `planejamento` ganha ano, versão, schema, origem, oficial e autoria. No Postgres, todas as tabelas do
Planejamento (tudo, menos as COMPARTILHADAS abaixo) passam para o schema `plan_<id>` do primeiro Planejamento
(`ALTER TABLE ... SET SCHEMA`: só metadado, os dados não são copiados); se o banco não tem nenhum Planejamento, cria o
"2026 Vrs. 01". Outros Planejamentos que já existam ficam sem schema até a inicialização separá-los
(`planejamento_copia.separar_legados`).

Convenção para as migrations seguintes: o Alembic roda cada revisão uma vez no schema comum e uma vez em cada schema de
Planejamento (`migrations/env.py`); a revisão decide pelo contexto — `op.get_context().version_table_schema` é None no
schema comum e o nome do schema no Planejamento — e mexe em tabelas compartilhadas só no primeiro e nas demais só no segundo.
"""
from typing import Sequence, Union

from alembic import op
import sqlalchemy as sa


# revision identifiers, used by Alembic.
revision: str = 'e8c1b7a45d93'
down_revision: Union[str, None] = 'd3f6b8a1c294'
branch_labels: Union[str, Sequence[str], None] = None
depends_on: Union[str, Sequence[str], None] = None

# Copiado de `planejamento_schema.COMPARTILHADAS` (a migration não depende do código que muda depois).
COMPARTILHADAS = ('usuario', 'registro_atividade', 'planejamento', 'planejamento_versao', 'alembic_version')


def upgrade() -> None:
    op.add_column('planejamento', sa.Column('ano', sa.Integer(), nullable=True))
    op.add_column('planejamento', sa.Column('numero_versao', sa.Integer(), nullable=True))
    op.add_column('planejamento', sa.Column('schema', sa.String(length=63), nullable=True))
    op.add_column('planejamento', sa.Column('origem_id', sa.Integer(), nullable=True))
    op.add_column('planejamento', sa.Column('oficial', sa.Boolean(), server_default=sa.false(), nullable=False))
    op.add_column('planejamento', sa.Column('criado_em', sa.DateTime(), server_default=sa.func.now(), nullable=False))
    op.add_column('planejamento', sa.Column('criado_por', sa.String(length=200), nullable=True))
    op.create_unique_constraint('uq_planejamento_schema', 'planejamento', ['schema'])
    op.create_foreign_key('fk_planejamento_origem', 'planejamento', 'planejamento', ['origem_id'], ['id'])

    bind = op.get_bind()
    if bind.dialect.name != 'postgresql':
        return
    comum = bind.execute(sa.text('select current_schema()')).scalar()

    primeiro = bind.execute(sa.text('select id from planejamento order by id limit 1')).scalar()
    if primeiro is None:
        primeiro = bind.execute(
            sa.text(
                "insert into planejamento (codigo, nome, ano, numero_versao, oficial) "
                "values ('P2026V01', 'Planejamento 2026 Vrs. 01', 2026, 1, true) returning id"
            )
        ).scalar()
    else:
        bind.execute(sa.text('update planejamento set oficial = true where id = :id'), {'id': primeiro})
    # mesma regra de `planejamento_schema.nome_do_schema`: fora do `public`, o nome do schema comum vira prefixo
    schema = f'plan_{primeiro}' if comum == 'public' else f'{comum}_plan_{primeiro}'
    bind.execute(sa.text('update planejamento set schema = :s where id = :id'), {'s': schema, 'id': primeiro})

    bind.execute(sa.text(f'create schema "{schema}"'))
    tabelas = bind.execute(sa.text('select tablename from pg_tables where schemaname = :s'), {'s': comum}).scalars().all()
    for tabela in tabelas:
        if tabela not in COMPARTILHADAS:
            bind.execute(sa.text(f'alter table "{comum}"."{tabela}" set schema "{schema}"'))
    # O Planejamento tem a própria versão do banco: as próximas revisões rodam também nele.
    bind.execute(sa.text(f'create table "{schema}".alembic_version (version_num varchar(32) not null primary key)'))
    bind.execute(sa.text(f'insert into "{schema}".alembic_version (version_num) values (:v)'), {'v': revision})


def downgrade() -> None:
    bind = op.get_bind()
    if bind.dialect.name == 'postgresql':
        comum = bind.execute(sa.text('select current_schema()')).scalar()
        schemas = bind.execute(sa.text('select schema from planejamento where schema is not null')).scalars().all()
        if len(schemas) > 1:
            raise RuntimeError(
                f'Há {len(schemas)} Planejamentos com schema próprio; exclua os extras antes de reverter esta migration'
            )
        for schema in schemas:
            tabelas = bind.execute(sa.text('select tablename from pg_tables where schemaname = :s'), {'s': schema}).scalars().all()
            for tabela in tabelas:
                if tabela != 'alembic_version':
                    bind.execute(sa.text(f'alter table "{schema}"."{tabela}" set schema "{comum}"'))
            bind.execute(sa.text(f'drop schema "{schema}" cascade'))
    op.drop_constraint('fk_planejamento_origem', 'planejamento', type_='foreignkey')
    op.drop_constraint('uq_planejamento_schema', 'planejamento', type_='unique')
    for coluna in ('criado_por', 'criado_em', 'oficial', 'origem_id', 'schema', 'numero_versao', 'ano'):
        op.drop_column('planejamento', coluna)
