import json
import logging

from psycopg2.errorcodes import DUPLICATE_COLUMN
from sqlalchemy import *
from sqlalchemy.engine import reflection

from alembic.migration import MigrationContext
from alembic.operations import Operations

from rhodecode.lib.dbmigrate.versions import _reset_base
from rhodecode.lib.jsonalchemy import MutationObj, JsonType
from rhodecode.model import meta, init_model_encryption


def upgrade(migrate_engine):
    """
    Upgrade operations go here.
    Don't create your own engine; bind migrate_engine to your metadata
    """
    _reset_base(migrate_engine)

    from rhodecode.lib.dbmigrate.schema import db_4_20_0_0 as db

    init_model_encryption(db)

    context = MigrationContext.configure(migrate_engine.connect())
    op = Operations(context)
    inspector = inspect(migrate_engine)

    pr_tables = [db.PullRequest.__table__, db.PullRequestVersion.__table__]
    for pr_table in pr_tables:
        existing_columns = [col["name"] for col in inspector.get_columns(pr_table.name)]
        new_column_name = "settings_json"
        if new_column_name not in existing_columns:
            with op.batch_alter_table(pr_table.name) as batch_op:
                new_column = Column(
                    new_column_name,
                    MutationObj.as_mutable(
                        JsonType(dialect_map=dict(mysql=UnicodeText(16384))),
                    ),
                    default=dict,
                )
                batch_op.add_column(new_column)

    _inherit_settings(db, meta.Session, op)


def downgrade(migrate_engine):
    pass


def _get_global_setting(repo):
    try:
        key = "%s_close_branch_before_merging" % repo.repo_type
        setting = next(filter(lambda s: s.app_settings_name == key, repo.settings))
        return setting.app_settings_value
    except StopIteration:
        return False


def _inherit_settings(models, _SESSION, op):
    _models = [models.PullRequest, models.PullRequestVersion]
    _seen_repos = {}

    for model in _models:
        for pr in _SESSION.query(model).filter(model.status != model.STATUS_CLOSED).all():
            repo = pr.target_repo
            repo_type = repo.repo_type
            close_branch_before_merging = False

            if repo_type in ["git", "hg"]:
                close_branch_before_merging = _seen_repos.get(repo.repo_id, _get_global_setting(repo))
                _seen_repos[repo.repo_id] = close_branch_before_merging

            json_settings = {"close_branch_before_merging": close_branch_before_merging}
            params = {"id": pr.pull_request_id, "value": json.dumps(json_settings)}
            query = text(
                """UPDATE %s
                   SET settings_json = :value
                   WHERE pull_request_id = :id"""
                % pr.__tablename__
            ).bindparams(**params)
            op.execute(query)

    _SESSION().commit()
