"""tres eixos (plano de contas / centro de custo / macroprocesso-processo-atividade) e classificacao fixo/variavel

Revision ID: 40787e081910
Revises: 811b8a8fef5a
Create Date: 2026-09-10 09:15:00.000000

"""
from typing import Sequence, Union

from alembic import op
import sqlalchemy as sa


# revision identifiers, used by Alembic.
revision: str = '40787e081910'
down_revision: Union[str, None] = '811b8a8fef5a'
branch_labels: Union[str, Sequence[str], None] = None
depends_on: Union[str, Sequence[str], None] = None


def upgrade() -> None:
    # --- novos cadastros dos 3 eixos ---
    op.create_table(
        'plano_de_contas',
        sa.Column('id', sa.Integer(), nullable=False),
        sa.Column('codigo', sa.String(length=20), nullable=False),
        sa.Column('nome', sa.String(length=200), nullable=False),
        sa.Column('tipo', sa.String(length=20), nullable=False),
        sa.Column('parent_id', sa.Integer(), nullable=True),
        sa.CheckConstraint("tipo in ('receita', 'custo', 'despesa', 'imposto')", name='ck_plano_de_contas_tipo'),
        sa.ForeignKeyConstraint(['parent_id'], ['plano_de_contas.id'], ),
        sa.PrimaryKeyConstraint('id'),
        sa.UniqueConstraint('codigo'),
    )
    op.create_table(
        'macroprocesso',
        sa.Column('id', sa.Integer(), nullable=False),
        sa.Column('codigo', sa.String(length=20), nullable=False),
        sa.Column('nome', sa.String(length=200), nullable=False),
        sa.PrimaryKeyConstraint('id'),
        sa.UniqueConstraint('codigo'),
    )

    # --- setor -> centro_de_custo ---
    op.rename_table('setor', 'centro_de_custo')

    # --- processo: passa a referenciar macroprocesso em vez de setor/centro_de_custo ---
    op.drop_constraint('processo_setor_id_fkey', 'processo', type_='foreignkey')
    op.drop_column('processo', 'setor_id')
    op.add_column('processo', sa.Column('macroprocesso_id', sa.Integer(), nullable=True))
    op.create_foreign_key(
        'processo_macroprocesso_id_fkey', 'processo', 'macroprocesso', ['macroprocesso_id'], ['id']
    )
    # backfill: processo pre-existente (dados de exemplo locais) ganha um Macroprocesso "a revisar"
    op.execute(
        """
        INSERT INTO macroprocesso (codigo, nome)
        SELECT 'MACRO_BACKFILL', 'Migrado automaticamente (revisar)'
        WHERE EXISTS (SELECT 1 FROM processo)
          AND NOT EXISTS (SELECT 1 FROM macroprocesso WHERE codigo = 'MACRO_BACKFILL')
        """
    )
    op.execute(
        """
        UPDATE processo SET macroprocesso_id = (SELECT id FROM macroprocesso WHERE codigo = 'MACRO_BACKFILL')
        WHERE macroprocesso_id IS NULL
        """
    )
    op.alter_column('processo', 'macroprocesso_id', nullable=False)

    # --- atividade: nivel 3 do rateio ABC, referenciada pelo roteiro ---
    op.create_table(
        'atividade',
        sa.Column('id', sa.Integer(), nullable=False),
        sa.Column('codigo', sa.String(length=20), nullable=False),
        sa.Column('processo_id', sa.Integer(), nullable=False),
        sa.Column('nome', sa.String(length=200), nullable=False),
        sa.ForeignKeyConstraint(['processo_id'], ['processo.id'], ),
        sa.PrimaryKeyConstraint('id'),
        sa.UniqueConstraint('codigo'),
    )

    # backfill: uma Atividade "a revisar" por Processo hoje referenciado por algum roteiro,
    # e remapeamento dos roteiros (ainda com a coluna processo_id) para essa atividade.
    op.execute(
        """
        INSERT INTO atividade (codigo, processo_id, nome)
        SELECT 'ATIV_BACKFILL_' || p.id, p.id, 'Migrado automaticamente (revisar)'
        FROM processo p
        WHERE EXISTS (
            SELECT 1 FROM roteiro_maquina rm WHERE rm.processo_id = p.id
            UNION SELECT 1 FROM roteiro_ambiente ra WHERE ra.processo_id = p.id
            UNION SELECT 1 FROM roteiro_mao_de_obra rmo WHERE rmo.processo_id = p.id
            UNION SELECT 1 FROM roteiro_insumo ri WHERE ri.processo_id = p.id
        )
        AND NOT EXISTS (SELECT 1 FROM atividade a WHERE a.codigo = 'ATIV_BACKFILL_' || p.id)
        """
    )
    for tabela in ('roteiro_maquina', 'roteiro_ambiente', 'roteiro_mao_de_obra', 'roteiro_insumo'):
        op.execute(
            f"""
            UPDATE {tabela} t SET processo_id = a.id
            FROM atividade a
            WHERE a.processo_id = t.processo_id AND a.codigo = 'ATIV_BACKFILL_' || t.processo_id
            """
        )

    # --- ambiente: ganha centro_de_custo + classificacao fixo/variavel ---
    op.add_column('ambiente', sa.Column('centro_de_custo_id', sa.Integer(), nullable=True))
    op.add_column('ambiente', sa.Column('classificacao', sa.String(length=20), nullable=True))
    op.create_foreign_key(
        'ambiente_centro_de_custo_id_fkey', 'ambiente', 'centro_de_custo', ['centro_de_custo_id'], ['id']
    )
    op.execute("UPDATE ambiente SET classificacao = 'fixo' WHERE classificacao IS NULL")
    op.create_check_constraint('ck_ambiente_classificacao', 'ambiente', "classificacao in ('fixo', 'variavel')")
    op.alter_column('ambiente', 'classificacao', nullable=False)

    # --- maquina: setor_id -> centro_de_custo_id + classificacao fixo/variavel ---
    op.drop_constraint('maquina_setor_id_fkey', 'maquina', type_='foreignkey')
    op.alter_column('maquina', 'setor_id', new_column_name='centro_de_custo_id')
    op.create_foreign_key(
        'maquina_centro_de_custo_id_fkey', 'maquina', 'centro_de_custo', ['centro_de_custo_id'], ['id']
    )
    op.add_column('maquina', sa.Column('classificacao', sa.String(length=20), nullable=True))
    op.execute("UPDATE maquina SET classificacao = 'fixo' WHERE classificacao IS NULL")
    op.create_check_constraint('ck_maquina_classificacao', 'maquina', "classificacao in ('fixo', 'variavel')")
    op.alter_column('maquina', 'classificacao', nullable=False)

    # --- funcionario: setor_id -> centro_de_custo_id + classificacao fixo/variavel ---
    op.drop_constraint('funcionario_setor_id_fkey', 'funcionario', type_='foreignkey')
    op.alter_column('funcionario', 'setor_id', new_column_name='centro_de_custo_id')
    op.create_foreign_key(
        'funcionario_centro_de_custo_id_fkey', 'funcionario', 'centro_de_custo', ['centro_de_custo_id'], ['id']
    )
    op.add_column('funcionario', sa.Column('classificacao', sa.String(length=20), nullable=True))
    op.execute("UPDATE funcionario SET classificacao = 'variavel' WHERE classificacao IS NULL")
    op.create_check_constraint('ck_funcionario_classificacao', 'funcionario', "classificacao in ('fixo', 'variavel')")
    op.alter_column('funcionario', 'classificacao', nullable=False)

    # --- roteiros: processo_id (ja remapeado para apontar a uma atividade) -> atividade_id ---
    for tabela in ('roteiro_maquina', 'roteiro_ambiente', 'roteiro_mao_de_obra', 'roteiro_insumo'):
        op.drop_constraint(f'{tabela}_processo_id_fkey', tabela, type_='foreignkey')
        op.alter_column(tabela, 'processo_id', new_column_name='atividade_id')
        op.create_foreign_key(
            f'{tabela}_atividade_id_fkey', tabela, 'atividade', ['atividade_id'], ['id']
        )


def downgrade() -> None:
    # remapeia atividade_id -> processo_id (da Atividade sendo removida) antes de renomear a coluna
    for tabela in ('roteiro_maquina', 'roteiro_ambiente', 'roteiro_mao_de_obra', 'roteiro_insumo'):
        op.execute(
            f"""
            UPDATE {tabela} t SET atividade_id = a.processo_id
            FROM atividade a
            WHERE a.id = t.atividade_id
            """
        )
        op.drop_constraint(f'{tabela}_atividade_id_fkey', tabela, type_='foreignkey')
        op.alter_column(tabela, 'atividade_id', new_column_name='processo_id')
        op.create_foreign_key(
            f'{tabela}_processo_id_fkey', tabela, 'processo', ['processo_id'], ['id']
        )

    op.drop_constraint('funcionario_centro_de_custo_id_fkey', 'funcionario', type_='foreignkey')
    op.drop_constraint('ck_funcionario_classificacao', 'funcionario', type_='check')
    op.drop_column('funcionario', 'classificacao')
    op.alter_column('funcionario', 'centro_de_custo_id', new_column_name='setor_id')
    op.create_foreign_key('funcionario_setor_id_fkey', 'funcionario', 'centro_de_custo', ['setor_id'], ['id'])

    op.drop_constraint('maquina_centro_de_custo_id_fkey', 'maquina', type_='foreignkey')
    op.drop_constraint('ck_maquina_classificacao', 'maquina', type_='check')
    op.drop_column('maquina', 'classificacao')
    op.alter_column('maquina', 'centro_de_custo_id', new_column_name='setor_id')
    op.create_foreign_key('maquina_setor_id_fkey', 'maquina', 'centro_de_custo', ['setor_id'], ['id'])

    op.drop_constraint('ambiente_centro_de_custo_id_fkey', 'ambiente', type_='foreignkey')
    op.drop_constraint('ck_ambiente_classificacao', 'ambiente', type_='check')
    op.drop_column('ambiente', 'classificacao')
    op.drop_column('ambiente', 'centro_de_custo_id')

    op.drop_table('atividade')

    op.drop_constraint('processo_macroprocesso_id_fkey', 'processo', type_='foreignkey')
    op.drop_column('processo', 'macroprocesso_id')
    # nullable=True: nao ha como recuperar o setor_id original (eixo removido do Processo)
    op.add_column('processo', sa.Column('setor_id', sa.Integer(), nullable=True))
    op.create_foreign_key('processo_setor_id_fkey', 'processo', 'centro_de_custo', ['setor_id'], ['id'])

    op.rename_table('centro_de_custo', 'setor')

    op.drop_table('macroprocesso')
    op.drop_table('plano_de_contas')
