# Generated by Django 2.2.11 on 2021-06-14 09:08
import hashlib

from django.db import connection, migrations

from tqdm import tqdm


# noinspection DuplicatedCode
def split_identifier(identifier):
    """
    Split an SQL identifier into a two element tuple of (namespace, name).

    The identifier could be a table, column, or sequence name might be prefixed
    by a namespace.
    """
    try:
        namespace, name = identifier.split('"."')
    except ValueError:
        namespace, name = "", identifier
    return namespace.strip('"'), name.strip('"')


def names_digest(*args, length):
    """
    Generate a 32-bit digest of a set of arguments that can be used to shorten
    identifying names.
    """
    # Copied from django code for compat, no danger just used for naming indexes.
    h = hashlib.md5()  # nosec
    for arg in args:
        h.update(arg.encode())
    return h.hexdigest()[:length]


def _django_index_name(table, field_names):
    table_name = f"database_table_{table.id}"
    return _copied_django_internal_index_name_calculator(table_name, field_names)


def _copied_django_internal_index_name_calculator(table_name, column_names, suffix=""):
    """
    COPIED FROM https://github.com/django/django/blob
    /ba9ced3e9a643a05bc521f0a2e6d02e3569de374/django/db/backends/base/schema.py#L989

    Generate a unique name for an index/unique constraint.
    The name is divided into 3 parts: the table name, the column names,
    and a unique digest and suffix.
    """
    _, table_name = split_identifier(table_name)
    hash_suffix_part = "%s%s" % (
        names_digest(table_name, *column_names, length=8),
        suffix,
    )
    max_length = connection.ops.max_name_length() or 200
    # If everything fits into max_length, use that name.
    index_name = "%s_%s_%s" % (table_name, "_".join(column_names), hash_suffix_part)
    if len(index_name) <= max_length:
        return index_name
    # Shorten a long suffix.
    if len(hash_suffix_part) > max_length / 3:
        hash_suffix_part = hash_suffix_part[: max_length // 3]
    other_length = (max_length - len(hash_suffix_part)) // 2 - 1
    index_name = "%s_%s_%s" % (
        table_name[:other_length],
        "_".join(column_names)[:other_length],
        hash_suffix_part,
    )
    # Prepend D if needed to prevent the name from starting with an
    # underscore or a number (not permitted on Oracle).
    if index_name[0] == "_" or index_name[0].isdigit():
        index_name = "D%s" % index_name[:-1]
    return index_name


def _add_index_for_field(schema_editor, table, field_names):
    table_name = f"database_table_{table.id}"
    index_name = _django_index_name(table, field_names)
    field_names_sql = ""
    for index, field_name in enumerate(field_names):
        comma = ", " if index < len(field_names) - 1 else ""
        field_names_sql += f'"{field_name}"{comma}'

    schema_editor.execute(
        (
            f"CREATE INDEX CONCURRENTLY IF NOT EXISTS "
            f'"{index_name}" ON "{table_name}"({field_names_sql})'
        )
    )


def _remove_index_for_field(schema_editor, table, field_names):
    index_name = _django_index_name(table, field_names)
    schema_editor.execute((f'DROP INDEX CONCURRENTLY IF EXISTS "{index_name}"'))


# noinspection PyPep8Naming
def forward(apps, schema_editor):
    Table = apps.get_model("database", "Table")

    for table in tqdm(
        Table.objects.all().order_by("id"),
        desc="Adding (order ASC, id ASC) index to all tables",
    ):
        _remove_index_for_field(schema_editor, table, ["order"])
        _add_index_for_field(schema_editor, table, ["order", "id"])


# noinspection PyPep8Naming
def reverse(apps, schema_editor):
    # We can't safely rollback the indexes made above as we can't tell which ones were
    # created separately from this migration by django itself.
    pass


class Migration(migrations.Migration):
    atomic = False

    dependencies = [
        ("database", "0051_gallery_view"),
    ]

    operations = [
        migrations.RunPython(forward, reverse),
    ]
