import os
from distutils.version import StrictVersion
from importlib import import_module

from django.conf import settings
from django.db.migrations.recorder import \
    MigrationRecorder as DjangoMigrationRecorder
from django.db.utils import ProgrammingError

import septentrion


fixtures_default_tpl = 'fixtures_{}.sql'
schema_default_tpl = 'schema_{}.sql'


class DBException(Exception):
    pass


def is_version(vstring):
    try:
        StrictVersion(vstring)
    except ValueError:
        return False
    return True


def list_dirs(root):
    return [d for d in os.listdir(root)
            if os.path.isdir(os.path.join(root, d))]


def list_files(root):
    return [d for d in os.listdir(root)
            if os.path.isfile(os.path.join(root, d))]


class MigrationRecorder(DjangoMigrationRecorder):
    def record_applied(self, app, name):
        """
        Records that a migration was applied.
        """
        # custom: do not ensure schema
        self.migration_qs.create(app=app, name=name)


def get_applied_versions(connection):
    """
    Return the list of applied versions.
    Reuse django migration table.
    """
    recorder = MigrationRecorder(connection)
    applied_versions = list(recorder.migration_qs.filter(
        app__in=septentrion.get_known_versions(
            MIGRATIONS_ROOT=settings.NORTH_MIGRATIONS_ROOT,
        )
    ).values_list(
        'app', flat=True).order_by('app').distinct())

    # sort versions
    applied_versions.sort(key=StrictVersion)
    return applied_versions


def get_current_version(connection):
    """
    Return the current version of the database.
    Return None if the schema is not inited.
    """
    try:
        import_string = settings.NORTH_CURRENT_VERSION_DETECTOR
    except AttributeError:
        import_string = (
            'django_north.management.migrations'
            '.get_current_version_from_table')

    module_path, factory_name = import_string.rsplit('.', 1)
    module = import_module(module_path)
    factory = getattr(module, factory_name)

    return factory(connection)


def get_current_version_from_table(connection):
    """
    Return the current version of the database, from sql_version table.
    Return None if the table does not exist (schema not inited).
    """
    with connection.cursor() as cursor:
        try:
            cursor.execute("SELECT version_num FROM sql_version;")
        except ProgrammingError:
            # table does not exist ?
            return None

        rows = cursor.fetchall()

    versions = [row[0] for row in rows if is_version(row[0])]
    if not versions:
        return None

    # sort versions
    versions.sort(key=StrictVersion)

    # return the last one
    return versions[-1]


def get_current_version_from_comment(connection):
    """
    Return the current version of the database, from django_site comment.
    Return None if the table django_site does not exist (schema not inited).
    """
    with connection.cursor() as cursor:
        try:
            cursor.execute(
                "SELECT obj_description('django_site'::regclass, 'pg_class');")
        except ProgrammingError:
            # table does not exist ?
            return None

        row = cursor.fetchone()
        comment = row[0]

    # no comment
    if comment is None:
        raise DBException('No comment found on django_site.')

    # parse comment
    if 'version ' not in comment:
        raise DBException("No version found in django_site's comment.")
    return comment.replace('version ', '').strip()


def get_applied_migrations(version, connection):
    """
    Return the list of applied migrations for the given version.
    Reuse django migration table.
    """
    recorder = MigrationRecorder(connection)
    return list(recorder.migration_qs.filter(app=version).values_list(
        'name', flat=True))


def is_manual_migration(file_handler):
    if '/manual/' in file_handler.name:
        return True

    if not file_handler.name.endswith('dml.sql'):
        return False

    for line in file_handler:
        if '--meta-psql:' in line:
            file_handler.seek(0)
            return True

    file_handler.seek(0)
    return False

@@ -3871,343 +3871,4 @@
 e))%0A
-%0A%0Adef is_manual_migration(file_handler):%0A    if '/manual/' in file_handler.name:%0A        return True%0A%0A    if not file_handler.name.endswith('dml.sql'):%0A        return False%0A%0A    for line in file_handler:%0A        if '--meta-psql:' in line:%0A            file_handler.seek(0)%0A            return True%0A%0A    file_handler.seek(0)%0A    return False%0A

delete unused function