"""card_attributes linked with card_model_attributes

Revision: 0040
Revision ID: 23e5cefde476
Revises: 9f215a58d65
Create Date: 2015-11-30 15:33:07.060545

"""

# revision identifiers, used by Alembic.
revision = '23e5cefde476'
down_revision = '9f215a58d65'
branch_labels = None
depends_on = None

from alembic import op
import sqlalchemy as sa
from sqlalchemy.dialects import postgresql

orgs_table = sa.Table(
    'organizations',
    sa.MetaData(),
    sa.Column('id', sa.Integer, primary_key=True),
)

cards_table = sa.Table(
    'cards',
    sa.MetaData(),
    sa.Column('id', sa.Integer, primary_key=True),
    sa.Column('owner_id', sa.Integer),
    sa.Column('model_id', sa.Integer),
    sa.Column('parents_ids', postgresql.ARRAY(sa.Integer)),
    sa.Column('default_child_card_model_id', sa.Integer)
)

card_enabled_models_table = sa.Table(
    'card_enabled_models',
    sa.MetaData(),
    sa.Column('model_id', sa.Integer),
    sa.Column('card_id', sa.Integer)
)

card_attributes_table = sa.Table(
    'card_attributes',
    sa.MetaData(),
    sa.Column('id', sa.Integer, primary_key=True),
    sa.Column('card_id', sa.Integer),
    sa.Column('name', sa.String),
    sa.Column('type_name', sa.String),
    sa.Column('card_model_attribute_id', sa.Integer)
)

card_models_table = sa.Table(
    'card_models',
    sa.MetaData(),
    sa.Column('id', sa.Integer, primary_key=True),
    sa.Column('owner_id', sa.Integer)
)


card_model_attributes_table = sa.Table(
    'card_model_attributes',
    sa.MetaData(),
    sa.Column('id', sa.Integer, primary_key=True),
    sa.Column('card_id', sa.Integer),
    sa.Column('card_model_id', sa.Integer),
    sa.Column('name', sa.String),
    sa.Column('title', sa.String),
    sa.Column('type_name', sa.String)
)

def upgrade():
    op.add_column('card_attributes', sa.Column('card_model_attribute_id', sa.Integer(), nullable=True))
    op.create_foreign_key('card_attribute_card_model_attribute_id', 'card_attributes', 'card_model_attributes', ['card_model_attribute_id'], ['id'], ondelete='CASCADE')

    conn = op.get_bind()
    for org in conn.execute(orgs_table.select()):
        model_attributes = {}
        for model in conn.execute(card_models_table.select().where(card_models_table.c.owner_id == org.id)):
            for model_attr in conn.execute(card_model_attributes_table.select().where(card_model_attributes_table.c.card_model_id == model.id)):
                key = model_attr.type_name + '::' + model_attr.name + '!!' + str(model_attr.card_model_id)
                if key not in model_attributes:
                    model_attributes[key] = model_attr.id

        def updateCardAttr(key, attr):
            keys = []
            keys.append(key)
            key2 = ''.join(key).replace('_', ' ')
            if key2 not in keys:
                keys.append(key2)
            key2 = ''.join(key).replace(' ', '_')
            if key2 not in keys:
                keys.append(key2)
            for k in keys:
                if k in model_attributes:
                    conn.execute(card_attributes_table.update().where(card_attributes_table.c.id == attr.id).values(card_model_attribute_id=model_attributes[k]))
                    return True
            return False

        models = {}
        enabled = {}
        org_models = []
        for card in conn.execute(cards_table.select().where(cards_table.c.owner_id == org.id)):
            top_level_card = card
            for attr in conn.execute(card_attributes_table.select().where(card_attributes_table.c.card_id == card.id)):
                if 'github' in attr.type_name or attr.name == 'github_branch' or 'gitlab_' in attr.name:
                    continue
                key = attr.type_name + '::' + attr.name + '!!' + str(card.model_id)
                if updateCardAttr(key, attr):
                    continue
                if len(card.parents_ids):
                    top_level_card = conn.execute(cards_table.select().where(cards_table.c.id == card.parents_ids[0])).first()
                if top_level_card.id not in models:
                    models[top_level_card.id] = []
                    models[top_level_card.id].append(top_level_card.model_id)
                    models[top_level_card.id].append(top_level_card.default_child_card_model_id)

                found = False
                for model_id in models[top_level_card.id]:
                    if updateCardAttr(attr.type_name + '::' + attr.name + '!!' + str(model_id), attr):
                        found = True
                        break
                if found:
                    continue
                if top_level_card.id not in enabled:
                    enabled[top_level_card.id] = []
                    for model in conn.execute(card_enabled_models_table.select().where(card_enabled_models_table.c.card_id == top_level_card.id)):
                        enabled[top_level_card.id].append(model.model_id)
                for model_id in enabled[top_level_card.id]:
                    if updateCardAttr(attr.type_name + '::' + attr.name + '!!' + str(model_id), attr):
                        found = True
                        break
                if found:
                    continue
                if not len(org_models):
                    for model in conn.execute(card_models_table.select().where(card_models_table.c.owner_id == org.id)):
                        org_models.append(model.id)
                for model_id in org_models:
                    if updateCardAttr(attr.type_name + '::' + attr.name + '!!' + str(model_id), attr):
                        found = True
                        break
                if found:
                    continue
                key = attr.type_name + '::' + attr.name + '!!extra:' + str(card.id)
                for extra_attr in conn.execute(card_model_attributes_table.select().where(sa.and_(card_model_attributes_table.c.card_id == card.id, card_model_attributes_table.c.card_model_id == None))):
                    if extra_attr.name == attr.name and extra_attr.type_name == attr.type_name:
                        model_attributes[key] = extra_attr.id
                        updateCardAttr(key, attr)
                        found = True
                        break
                if found:
                    continue
                res = conn.execute(card_model_attributes_table.insert().values(card_id=card.id, name=attr.name, title=attr.name, type_name=attr.type_name))
                model_attributes[key] = res.inserted_primary_key[0]
                updateCardAttr(key, attr)


def downgrade():
    op.drop_constraint('card_attribute_card_model_attribute_id', 'card_attributes', type_='foreignkey')
    op.drop_column('card_attributes', 'card_model_attribute_id')
