"""fix_models_attribute_groups

Revision: 0114
Revision ID: 4b8f6f8c1e02
Revises: 4d7f1ad39d84
Create Date: 2018-03-02 14:28:19.346050

"""

# revision identifiers, used by Alembic.
revision = '4b8f6f8c1e02'
down_revision = '4d7f1ad39d84'
branch_labels = None
depends_on = None

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

enabled_models = sa.Table(
    'project_enabled_models',
    sa.MetaData(),
    sa.Column('project_id', sa.Integer),
    sa.Column('model_id', sa.Integer)
)

models = sa.Table(
    'card_models',
    sa.MetaData(),
    sa.Column('id', sa.Integer, primary_key=True),
    sa.Column('attribute_group_id', sa.Integer),
    sa.Column('cache_id', sa.String),
    sa.Column('copied_from_id', sa.Integer)
)

attribute_groups = sa.Table(
    'attribute_groups',
    sa.MetaData(),
    sa.Column('id', sa.Integer, primary_key=True),
    sa.Column('owner_id', sa.Integer),
    sa.Column('created_at', sa.DateTime),
    sa.Column('shared_context', sa.PickleType),
    sa.Column('containers', postgresql.JSONB())
)

attributes = sa.Table(
    'attributes',
    sa.MetaData(),
    sa.Column('id', sa.Integer, primary_key=True),
    sa.Column('card_model_id', sa.Integer),
    sa.Column('card_id', sa.Integer),
    sa.Column('name', sa.String),
    sa.Column('description', sa.String),
    sa.Column('type_name', sa.String),
    sa.Column('container', sa.String),
    sa.Column('position', sa.Integer),
    sa.Column('options', sa.PickleType),
    sa.Column('default_value', sa.PickleType),
    sa.Column('is_from_builtin', sa.Boolean),
    sa.Column('show_on_tile', sa.Boolean),
    sa.Column('group_id', sa.Integer)
)

group_types = sa.Table(
    'card_group_types',
    sa.MetaData(),
    sa.Column('id', sa.Integer, primary_key=True),
    sa.Column('created_at', sa.DateTime),
    sa.Column('updated_at', sa.DateTime),
    sa.Column('created_by_id', sa.Integer),
    sa.Column('owner_id', sa.Integer),
    sa.Column('name', sa.String),
    sa.Column('description', sa.Text),
    sa.Column('scope', sa.String),
    sa.Column('one_group_per_card', sa.Boolean),
    sa.Column('singular_name', sa.String),
    sa.Column('plural_name', sa.String),
    sa.Column('icon', sa.String),
    sa.Column('builtin', sa.String),
    sa.Column('display_on_card', sa.String),
    sa.Column('auto_random_color', sa.Boolean),
    sa.Column('cache_id', sa.String),
    sa.Column('auto_enabled_on_new_projects', sa.Boolean),
    sa.Column('is_project_specific', sa.Boolean),
    sa.Column('auto_assign_period', sa.String),
    sa.Column('attribute_id', sa.Integer)
)

groups = sa.Table(
    'card_groups',
    sa.MetaData(),
    sa.Column('id', sa.Integer, primary_key=True),
    sa.Column('group_type_id', sa.Integer),
    sa.Column('parent_id', sa.Integer),
    sa.Column('cache_id', sa.String)
)

cards = sa.Table(
    'cards',
    sa.MetaData(),
    sa.Column('id', sa.Integer, primary_key=True),
    sa.Column('project_id', sa.Integer)
)

card_attributes = sa.Table(
    'card_attributes',
    sa.MetaData(),
    sa.Column('id', sa.Integer, primary_key=True),
    sa.Column('card_id', sa.Integer),
    sa.Column('attribute_id', sa.Integer)
)

def upgrade():
    update_models = []
    update_groups = []
    update_card_attributes = []

    conn = op.get_bind()

    # Attributes without attribute groups
    conn.execute(attributes.delete().where(attributes.c.group_id == None))

    # Models without attribute groups
    q_models = sa.text("""
        SELECT DISTINCT m.id, m.owner_id
        FROM card_models m, card_models m2
        WHERE m2.copied_from_id = m.id
        AND m.attribute_group_id IS NULL
        ORDER BY m.id
    """)
    for model in conn.execute(q_models):
        other_attribute_group_id = sa.select([models.c.attribute_group_id]).where(sa.and_(
            models.c.copied_from_id == model[0],
            models.c.attribute_group_id != None
        )).limit(1)
        attr_group = conn.execute(attribute_groups.select().where(attribute_groups.c.id == other_attribute_group_id)).first()
        if attr_group:
            res_attr_group = conn.execute(attribute_groups.insert().values(
                owner_id=model[1],
                created_at=datetime.utcnow(),
                shared_context=attr_group[3],
                containers=attr_group[4]))
            attr_group_id = res_attr_group.inserted_primary_key[0]
            update_models.append({'model_id': model[0], 'attr_group_id': attr_group_id, 'cache_id': uuid.uuid4()})
            for attr in conn.execute(attributes.select().where(attributes.c.group_id == attr_group[0])):
                if attr[12] is None:
                    continue
                res_attrs = conn.execute(attributes.insert().values(
                    card_model_id = attr[1],
                    card_id = attr[2],
                    name = attr[3],
                    description = attr[4],
                    type_name = attr[5],
                    container = attr[6],
                    position = attr[7],
                    options = attr[8],
                    default_value = attr[9],
                    is_from_builtin = attr[10],
                    show_on_tile = attr[11],
                    group_id = attr_group_id))
                attribute_id = res_attrs.inserted_primary_key[0]
                if attr[5] == 'group_type':
                    group_type = conn.execute(group_types.select().where(group_types.c.attribute_id == attr[0])).first()
                    res_gts = conn.execute(group_types.insert().values(
                        created_at = datetime.utcnow(),
                        created_by_id = group_type[3],
                        owner_id = model[1],
                        name = group_type[5],
                        description = group_type[6],
                        scope = group_type[7],
                        one_group_per_card = group_type[8],
                        singular_name = group_type[9],
                        plural_name = group_type[10],
                        icon = group_type[11],
                        builtin = group_type[12],
                        display_on_card = group_type[13],
                        auto_random_color = group_type[14],
                        cache_id = uuid.uuid4(),
                        auto_enabled_on_new_projects = group_type[16],
                        is_project_specific = group_type[17],
                        auto_assign_period = group_type[18],
                        attribute_id = attribute_id))

    # Models with duplicate attribute groups
    q_models = sa.text("""
        SELECT m.id, m.attribute_group_id, m.owner_id, pem.project_id
        FROM card_models m, project_enabled_models pem
        WHERE m.id = pem.model_id
        AND attribute_group_id IN (
                SELECT attribute_group_id from card_models
                WHERE attribute_group_id IS NOT NULL
                GROUP BY attribute_group_id
                HAVING count(*) > 1
        )
        AND copied_from_id IS NOT NULL
        ORDER BY attribute_group_id, id
    """)
    for model in conn.execute(q_models):
        attr_group = conn.execute(attribute_groups.select().where(attribute_groups.c.id == model[1])).first()
        if attr_group:
            project_id = model[3]
            res_attributes = conn.execute(attributes.select().where(attributes.c.group_id == model[1]))
            attr_group_id = None
            if res_attributes.rowcount > 0:
                res_attr_group = conn.execute(attribute_groups.insert().values(
                    owner_id=model[2],
                    created_at=datetime.utcnow(),
                    shared_context=attr_group[3],
                    containers=attr_group[4]))
                attr_group_id = res_attr_group.inserted_primary_key[0]
            update_models.append({'model_id': model[0], 'attr_group_id': attr_group_id, 'cache_id': uuid.uuid4()})
            if res_attributes.rowcount == 0:
                continue
            for attr in res_attributes:
                if attr[12] is None:
                    continue
                res_attrs = conn.execute(attributes.insert().values(
                    card_model_id = attr[1],
                    card_id = attr[2],
                    name = attr[3],
                    description = attr[4],
                    type_name = attr[5],
                    container = attr[6],
                    position = attr[7],
                    options = attr[8],
                    default_value = attr[9],
                    is_from_builtin = attr[10],
                    show_on_tile = attr[11],
                    group_id = attr_group_id))
                attribute_id = res_attrs.inserted_primary_key[0]
                if attr[5] == 'group_type':
                    group_type = conn.execute(group_types.select().where(group_types.c.attribute_id == attr[0])).first()
                    res_gt = conn.execute(group_types.insert().values(
                            created_at = datetime.utcnow(),
                            created_by_id = group_type[3],
                            owner_id = model[2],
                            name = group_type[5],
                            description = group_type[6],
                            scope = group_type[7],
                            one_group_per_card = group_type[8],
                            singular_name = group_type[9],
                            plural_name = group_type[10],
                            icon = group_type[11],
                            builtin = group_type[12],
                            display_on_card = group_type[13],
                            auto_random_color = group_type[14],
                            cache_id = uuid.uuid4(),
                            auto_enabled_on_new_projects = group_type[16],
                            is_project_specific = group_type[17],
                            auto_assign_period = group_type[18],
                            attribute_id = attribute_id))
                    gt_id = res_gt.inserted_primary_key[0]
                    q_groups = sa.text("""
                        SELECT g.id
                        FROM card_groups g, cards c, card_group_types gt, attributes a, attribute_groups ag, card_models m, project_enabled_models pem, projects p
                        WHERE g.parent_id = c.id
                        AND g.group_type_id = gt.id
                        AND gt.attribute_id = a.id
                        AND a.group_id = ag.id
                        AND ag.id = m.attribute_group_id
                        AND m.id = pem.model_id
                        AND c.project_id = p.id
                        AND m.is_project_specific is true
                        AND pem.project_id != c.project_id
                        AND gt.id = {0}
                        AND p.id = {1}
                    """.format(group_type[0], project_id))
                    for group in conn.execute(q_groups):
                        update_groups.append({'g_id': group[0], 'gt_id': gt_id, 'cache_id': uuid.uuid4()})
                else:
                    update_card_attributes.append({'old_a_id': attr[0], 'new_a_id': attribute_id, 'p_id': project_id})

    if update_models:
        conn.execute(models.update().where(models.c.id==sa.bindparam('model_id')).values(
            attribute_group_id=sa.bindparam('attr_group_id'), cache_id=sa.bindparam('cache_id')), update_models)

    if update_groups:
        conn.execute(groups.update().where(groups.c.id==sa.bindparam('g_id')).values(
            group_type_id=sa.bindparam('gt_id'), cache_id=sa.bindparam('cache_id')), update_groups)

    if update_card_attributes:
        conn.execute(card_attributes.update().where(sa.and_(card_attributes.c.card_id==cards.c.id, cards.c.project_id==sa.bindparam('p_id'), card_attributes.c.attribute_id==sa.bindparam('old_a_id'))).values(
            attribute_id=sa.bindparam('new_a_id')), update_card_attributes)


def downgrade():
    pass
