# -*- coding: utf-8 -*-
import os
import re
import hashlib
from typing import Any, Optional
from importlib import import_module
from six import text_type
from six.moves import cStringIO as StringIO

from django.db import connections, DEFAULT_DB_ALIAS
from django.apps import apps
from django.core.management import call_command
from django.utils.module_loading import module_has_submodule

def database_exists(database_name, **options):
    # type: (text_type, **Any) -> bool
    db = options.get('database', DEFAULT_DB_ALIAS)
    connection = connections[db]

    with connection.cursor() as cursor:
        cursor.execute("SELECT 1 from pg_database WHERE datname='{}';".format(database_name))
        return_value = bool(cursor.fetchone())
    connections.close_all()
    return return_value

def get_migration_status(**options):
    # type: (**Any) -> str
    verbosity = options.get('verbosity', 1)

    for app_config in apps.get_app_configs():
        if module_has_submodule(app_config.module, "management"):
            import_module('.management', app_config.name)

    app_labels = [options['app_label']] if options.get('app_label') else None
    db = options.get('database', DEFAULT_DB_ALIAS)
    out = StringIO()
    call_command(
        'showmigrations',
        '--list',
        app_labels=app_labels,
        database=db,
        no_color=options.get('no_color', False),
        settings=options.get('settings', os.environ['DJANGO_SETTINGS_MODULE']),
        stdout=out,
        traceback=options.get('traceback', True),
        verbosity=verbosity,
    )
    connections.close_all()
    out.seek(0)
    output = out.read()
    return re.sub('\x1b\[(1|0)m', '', output)

def are_migrations_the_same(migration_file, **options):
    # type: (text_type, **Any) -> bool
    if not os.path.exists(migration_file):
        return False

    with open(migration_file) as f:
        migration_content = f.read()
    return migration_content == get_migration_status(**options)

def _check_hash(source_hash_file, target_hash_file, **options):
    # type: (text_type, text_type, **Any) -> bool
    """
    This function has a side effect of creating a new hash file or
    updating the old hash file.
    """
    with open(target_hash_file) as f:
        target_hash_content = hashlib.sha1(f.read().encode('utf8')).hexdigest()

    if os.path.exists(source_hash_file):
        with open(source_hash_file) as f:
            source_hash_content = f.read().strip()
    else:
        source_hash_content = None

    with open(source_hash_file, 'a+') as f:
        f.truncate(0)
        f.write(target_hash_content)
        return source_hash_content == target_hash_content

def populate_db_hash_is_same(hash_file, **options):
    # type: (text_type, **Any) -> bool
    populate_db = 'zilencer/management/commands/populate_db.py'
    return _check_hash(hash_file, populate_db, **options)

def postgres_init_test_db_hash_is_same(hash_file, **options):
    # type: (text_type, **Any) -> bool
    postgres_init_test_db = 'tools/setup/postgres-init-test-db'
    return _check_hash(hash_file, postgres_init_test_db, **options)

def is_template_database_current(
        database_name='zulip_test_template',
        migration_status='var/migration-status',
        settings='zproject.test_settings',
        populate_db_hash='var/populate_db_hash',
        postgres_init_test_db_hash='var/postgres_init_test_db_hash'
    ):
    # type: (Optional[text_type], Optional[text_type], Optional[text_type], Optional[text_type], Optional[text_type]) -> bool
    if database_exists(database_name):
        # To ensure Python evaluates all 3 tests (and thus creates the
        # hash files about the current state), we evaluate them in a
        # list and then compare, rather than just returning the
        # boolean `and` of the 3 values.
        results = [are_migrations_the_same(migration_status, settings=settings),
                   populate_db_hash_is_same(populate_db_hash),
                   postgres_init_test_db_hash_is_same(postgres_init_test_db_hash)]
        return False not in results

    return False

@@ -383,16 +383,101 @@
 module%0A%0A
+FILENAME_SPLITTER = re.compile('%5B%5CW%5C-_%5D')%0ATEST_DB_STATUS_DIR = 'var/test_db_status'%0A%0A
 def data
@@ -2060,24 +2060,264 @@
 **options)%0A%0A
+def _get_hash_file_path(source_file_path):%0A    # type: (str) -%3E str%0A    basename = os.path.basename(source_file_path)%0A    filename = '_'.join(FILENAME_SPLITTER.split(basename)).lower()%0A    return os.path.join(TEST_DB_STATUS_DIR, filename)%0A%0A
 def _check_h
@@ -2385,28 +2385,16 @@
 e: (
-text_type, text_type
+str, str
 , **
@@ -2517,24 +2517,86 @@
 le.%0A    %22%22%22%0A
+    source_hash_file = _get_hash_file_path(target_hash_file)%0A%0A
     with ope
@@ -3868,33 +3868,27 @@
 %5D, Optional%5B
-text_type
+str
 %5D, Optional%5B
@@ -3887,25 +3887,19 @@
 ptional%5B
-text_type
+str
 %5D) -%3E bo
@@ -3897,24 +3897,110 @@
 r%5D) -%3E bool%0A
+%0A    if not os.path.exists(TEST_DB_STATUS_DIR):%0A        os.mkdir(TEST_DB_STATUS_DIR)%0A%0A
     if datab

Create hashes in var/test_db_status.