"""convert_group_type_scope

Revision: 0132
Revision ID: 4c4163218424
Revises: 3feaab6b3df
Create Date: 2018-04-17 11:21:29.082214

"""

# revision identifiers, used by Alembic.
revision = '4c4163218424'
down_revision = '3feaab6b3df'
branch_labels = None
depends_on = None

from alembic import op
import sqlalchemy as sa

card_group_types = sa.Table(
    'card_group_types',
    sa.MetaData(),
    sa.Column('id', sa.Integer, primary_key=True),
    sa.Column('scope', sa.String),
    sa.Column('cache_id', sa.String)
)

card_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('title', sa.String),
    sa.Column('cache_id', sa.String)
)

card_groups_cards = sa.Table(
    'card_groups_cards',
    sa.MetaData(),
    sa.Column('id', sa.Integer, primary_key=True),
    sa.Column('card_id', sa.Integer),
    sa.Column('group_id', sa.Integer)
)

def upgrade():
    conn = op.get_bind()

    # select group types which have groups with only one parent_id or no groups at all
    context_to_convert_directly_query = """
        select cgt.id from card_group_types cgt
        left join (select group_type_id, count(distinct parent_id) c from card_groups group by group_type_id) cg on cg.group_type_id = cgt.id
        where cgt.scope = 'card' and (cg.c <= 1 or cg.c is null)
    """

    updates = [{'gtid': r[0]} for r in conn.execute(context_to_convert_directly_query)]
    if updates:
        conn.execute(card_group_types.update().where(card_group_types.c.id==sa.bindparam('gtid')).values(
            scope='project', cache_id=sa.func.uuid_generate_v4()), updates)


    groups_attr_to_convert_directly_query = """
        select cgt.id from card_group_types cgt
        left join (select group_type_id, count(distinct parent_id) c from card_groups group by group_type_id) cg on cg.group_type_id = cgt.id
        where cgt.scope = 'project' and cgt.attribute_id is not null and (cg.c <= 1 or cg.c is null)
    """

    updates = [{'gtid': r[0]} for r in conn.execute(groups_attr_to_convert_directly_query)]
    if updates:
        conn.execute(card_group_types.update().where(card_group_types.c.id==sa.bindparam('gtid')).values(
            scope='org', cache_id=sa.func.uuid_generate_v4()), updates)
        conn.execute(card_groups.update().where(card_groups.c.group_type_id==sa.bindparam('gtid')).values(
            parent_id=None, cache_id=sa.func.uuid_generate_v4()), updates)


    groups_attr_to_process_query = """
        select cgt.id from card_group_types cgt
        left join (select group_type_id, count(distinct parent_id) c from card_groups group by group_type_id) cg on cg.group_type_id = cgt.id
        where cgt.scope = 'project' and cgt.attribute_id is not null and cg.c > 1
    """

    scope_updates = []
    parent_updates = []
    remap_updates = []
    for r in conn.execute(groups_attr_to_process_query):
        scope_updates.append({'gtid': r[0]})
        group_by_titles = {}
        for g in conn.execute(card_groups.select().where(card_groups.c.group_type_id==r[0])):
            group_by_titles.setdefault(g.title, []).append(g)
        for title, groups in group_by_titles.items():
            parent_updates.append({'gid': groups[0].id})
            if len(groups) == 1:
                continue
            for g in groups[1:]:
                remap_updates.append({'from_id': g.id, 'to_id': groups[0].id})

    if scope_updates:
        conn.execute(card_group_types.update().where(card_group_types.c.id==sa.bindparam('gtid')).values(
            scope='org', cache_id=sa.func.uuid_generate_v4()), scope_updates)
    if remap_updates:
        cgc1 = card_groups_cards.alias()
        cgc2 = card_groups_cards.alias()
        exclude_cards = sa.select([cgc1.c.card_id]).select_from(
            cgc1.join(cgc2, cgc2.c.card_id==cgc1.c.card_id)
        ).where(
            sa.and_(cgc1.c.group_id==sa.bindparam('from_id'), cgc2.c.group_id==sa.bindparam('to_id'))
        )

        conn.execute(card_groups_cards.update().where(sa.and_(
            card_groups_cards.c.group_id==sa.bindparam('from_id'),
            ~card_groups_cards.c.card_id.in_(exclude_cards)
        )).values(group_id=sa.bindparam('to_id')), remap_updates)

        conn.execute(card_groups.delete().where(card_groups.c.id==sa.bindparam('from_id')), remap_updates)
    if parent_updates:
        conn.execute(card_groups.update().where(card_groups.c.id==sa.bindparam('gid')).values(
            parent_id=None, cache_id=sa.func.uuid_generate_v4()), parent_updates)

    op.execute("""
        WITH positions as (
            SELECT id, row_number() OVER (partition by group_type_id order by position) AS p
            FROM card_groups WHERE NOT is_archived
        ) UPDATE card_groups SET position = positions.p - 1 FROM positions, card_group_types cgt
        WHERE cgt.id = card_groups.group_type_id AND cgt.attribute_id IS NOT NULL AND positions.id = card_groups.id
    """)

def downgrade():
    pass
