from typing import Callable, Optional

from django.db import connection

from ._version import parse_version


def get_schema_version():
    """
    Get the schema version from a legacy database.

    Don't use django models as they might break in the future. Our concern is to upgrade
    the legacy database schema to the point where django is in charge of the migrations.
    """

    if "cc_pref" not in connection.introspection.table_names():
        return None

    with connection.cursor() as cursor:
        cursor.execute("SELECT valstr FROM cc_pref WHERE keystr = 'schema_version'")
        row = cursor.fetchone()
        return row[0] if row else None


def set_schema_version(cursor, version: str):
    cursor.execute(
        """
        UPDATE cc_pref
        SET valstr = %s
        WHERE keystr = 'schema_version';
        """,
        [version],
    )
    if not cursor.rowcount:
        cursor.execute(
            """
            INSERT INTO cc_pref (keystr, valstr)
            VALUES ('schema_version', %s);
            """,
            [version],
        )


def legacy_migration_factory(
    target: str,
    before: Optional[Callable] = None,
    sql: Optional[str] = None,
    after: Optional[Callable] = None,
    reverse: bool = False,
):
    target_version = parse_version(target)

    def inner(_apps, _schema_editor):
        current = get_schema_version()
        if current is None:
            raise Exception("current schema version was not found!")

        current_version = parse_version(current)
        if current_version >= target_version and not reverse:
            return

        if current_version < target_version and reverse:
            return

        with connection.cursor() as cursor:
            if before is not None:
                before(cursor)

            if sql is not None:
                cursor.execute(sql)

            if after is not None:
                after(cursor)

            set_schema_version(cursor, version=target)

    return inner
