# -*- coding: utf-8 -*-
from io import StringIO

from django.core.management import CommandError, call_command
from django.test import TestCase
from django.test.utils import override_settings

from unittest.mock import patch


MYSQL_DATABASE_SETTINGS = {
    "ENGINE": "django.db.backends.mysql",
    "NAME": "dbatabase",
    "USER": "foo",
    "PASSWORD": "bar",
    "HOST": "127.0.0.1",
    "PORT": "3306",
}

SQLITE3_DATABASE_SETTINGS = {
    "ENGINE": "django.db.backends.sqlite3",
    "NAME": "db.sqlite3",
}

POSTGRESQL_DATABASE_SETTINGS = {
    "ENGINE": "django.db.backends.postgresql",
    "NAME": "database",
    "USER": "foo",
    "PASSWORD": "bar",
    "HOST": "localhost",
    "PORT": "5432",
}

POSTGRESQL_PSYCOPG2_DATABASE_SETTINGS = {
    "ENGINE": "django.db.backends.postgresql_psycopg2",
    "NAME": "database",
    "USER": "foo",
    "PASSWORD": "bar",
    "HOST": "localhost",
    "PORT": "5432",
}

POSTGIS_DATABASE_SETTINGS = {
    "ENGINE": "django.db.backends.postgis",
    "NAME": "database",
    "USER": "foo",
    "PASSWORD": "bar",
    "HOST": "localhost",
}

POSTGIS_WITH_PORT_DATABASE_SETTINGS = {
    "ENGINE": "django.db.backends.postgis",
    "NAME": "database",
    "USER": "foo",
    "PASSWORD": "bar",
    "HOST": "localhost",
    "PORT": 5432,
}


class SqlDsnExceptionsTests(TestCase):
    """Tests for sqldsn management command exceptions."""

    @override_settings(DATABASES={})
    def test_should_raise_CommandError_if_unknown_database_does_not_exist(self):
        with self.assertRaisesRegex(CommandError, "Unknown database unknown"):
            call_command("sqldsn", "--database=unknown")


class SqlDsnTests(TestCase):
    """Tests for sqldsn management command."""

    maxDiff = None

    @override_settings(DATABASES={"default": SQLITE3_DATABASE_SETTINGS})
    @patch("sys.stdout", new_callable=StringIO)
    def test_should_print_info_for_default_sqlite3_database(self, m_stdout):
        expected_result = """DSN for database 'default' with engine 'django.db.backends.sqlite3':
db.sqlite3
"""
        call_command("sqldsn")

        self.assertEqual(expected_result, m_stdout.getvalue())

    @override_settings(DATABASES={"default": SQLITE3_DATABASE_SETTINGS})
    @patch("sys.stdout", new_callable=StringIO)
    def test_should_print_uri_for_default_sqlite3_database(self, m_stdout):
        expected_result = """DSN for database 'default' with engine 'django.db.backends.sqlite3':
sqlite:///db.sqlite3
"""
        call_command("sqldsn", "--style=uri")

        self.assertEqual(expected_result, m_stdout.getvalue())

    @override_settings(DATABASES={"default": MYSQL_DATABASE_SETTINGS})
    @patch("sys.stdout", new_callable=StringIO)
    def test_should_print_quiet_info_for_mysql_database(self, m_stdout):
        expected_result = """host="127.0.0.1", db="dbatabase", user="foo", passwd="bar", port="3306"
"""
        call_command("sqldsn", "-q")

        self.assertEqual(expected_result, m_stdout.getvalue())

    @override_settings(DATABASES={"default": MYSQL_DATABASE_SETTINGS})
    @patch("sys.stdout", new_callable=StringIO)
    def test_should_print_quiet_uri_for_mysql_database(self, m_stdout):
        expected_result = """mysql://foo:bar@127.0.0.1:3306/dbatabase
"""
        call_command("sqldsn", "-q", "--style=uri")

        self.assertEqual(expected_result, m_stdout.getvalue())

    @override_settings(DATABASES={"default": MYSQL_DATABASE_SETTINGS})
    @patch("sys.stdout", new_callable=StringIO)
    def test_should_print_quiet_args_for_mysql_database(self, m_stdout):
        expected_result = """-h "127.0.0.1" -D "dbatabase" -u "foo" -p "bar" -P 3306
"""
        call_command("sqldsn", "-q", "--style=args")

        self.assertEqual(expected_result, m_stdout.getvalue())

    @override_settings(DATABASES={"default": POSTGRESQL_DATABASE_SETTINGS})
    @patch("sys.stdout", new_callable=StringIO)
    def test_should_print_all_info_for_postgresql_database(self, m_stdout):
        expected_result = """DSN for database 'default' with engine 'django.db.backends.postgresql':
host='localhost' dbname='database' user='foo' password='bar' port='5432'
"""

        call_command("sqldsn", "-a")

        self.assertEqual(expected_result, m_stdout.getvalue())

    @override_settings(DATABASES={"default": POSTGRESQL_PSYCOPG2_DATABASE_SETTINGS})
    @patch("sys.stdout", new_callable=StringIO)
    def test_should_print_info__with_kwargs_style_for_postgresql_psycopg2_database(
        self, m_stdout
    ):
        expected_result = """DSN for database 'default' with engine 'django.db.backends.postgresql_psycopg2':
host='localhost', database='database', user='foo', password='bar', port='5432'
"""

        call_command("sqldsn", "--style=kwargs")

        self.assertEqual(expected_result, m_stdout.getvalue())

    @override_settings(DATABASES={"default": POSTGIS_WITH_PORT_DATABASE_SETTINGS})
    @patch("sys.stdout", new_callable=StringIO)
    def test_should_print_info__with_uri_style_for_postgis_database(self, m_stdout):
        expected_result = """DSN for database 'default' with engine 'django.db.backends.postgis':
postgresql://foo:bar@localhost:5432/database
"""

        call_command("sqldsn", "--style=uri")

        self.assertEqual(expected_result, m_stdout.getvalue())

    @override_settings(DATABASES={"default": POSTGIS_DATABASE_SETTINGS})
    @patch("sys.stdout", new_callable=StringIO)
    def test_should_print_info__with_uri_style_without_port_for_postgis_database(
        self, m_stdout
    ):
        expected_result = """DSN for database 'default' with engine 'django.db.backends.postgis':
postgresql://foo:bar@localhost/database
"""

        call_command("sqldsn", "--style=uri")

        self.assertEqual(expected_result, m_stdout.getvalue())

    @override_settings(DATABASES={"default": POSTGRESQL_DATABASE_SETTINGS})
    @patch("sys.stdout", new_callable=StringIO)
    def test_should_print_info_with_pgpass_style_and_quiet_option_for_postgresql_database(
        self, m_stdout
    ):
        expected_result = "localhost:5432:database:foo:bar\n"

        call_command("sqldsn", "--style=pgpass", "-q")

        self.assertEqual(expected_result, m_stdout.getvalue())

    @override_settings(
        DATABASES={
            "default": POSTGRESQL_DATABASE_SETTINGS,
            "slave": MYSQL_DATABASE_SETTINGS,
            "test": SQLITE3_DATABASE_SETTINGS,
            "unknown": {
                "ENGINE": "django.db.backends.unknown",
            },
        }
    )
    @patch("sys.stdout", new_callable=StringIO)
    def test_should_print_info_for_all_databases(self, m_stdout):
        default_postgresql = """DSN for database 'default' with engine 'django.db.backends.postgresql':
host='localhost' dbname='database' user='foo' password='bar' port='5432'"""
        slave_mysql = '''DSN for database 'slave' with engine 'django.db.backends.mysql':
host="127.0.0.1", db="dbatabase", user="foo", passwd="bar", port="3306"'''
        test_sqlite3 = """DSN for database 'test' with engine 'django.db.backends.sqlite3':
db.sqlite3"""
        unknown = """DSN for database 'unknown' with engine 'django.db.backends.unknown':
Unknown database, can't generate DSN"""

        call_command("sqldsn", "--all")

        self.assertIn(default_postgresql, m_stdout.getvalue())
        self.assertIn(slave_mysql, m_stdout.getvalue())
        self.assertIn(test_sqlite3, m_stdout.getvalue())
        self.assertIn(unknown, m_stdout.getvalue())

    @override_settings(
        DATABASES={
            "default": POSTGRESQL_DATABASE_SETTINGS,
            "slave": MYSQL_DATABASE_SETTINGS,
            "test": SQLITE3_DATABASE_SETTINGS,
            "unknown": {
                "ENGINE": "django.db.backends.unknown",
            },
        }
    )
    @patch("sys.stdout", new_callable=StringIO)
    def test_should_print_info_with_all_style_for_all_databases(self, m_stdout):
        default_postgresql = """DSN for database 'default' with engine 'django.db.backends.postgresql':
host='localhost' dbname='database' user='foo' password='bar' port='5432'
host='localhost', database='database', user='foo', password='bar', port='5432'
postgresql://foo:bar@localhost:5432/database
localhost:5432:database:foo:bar"""
        slave_mysql = '''DSN for database 'slave' with engine 'django.db.backends.mysql':
host="127.0.0.1", db="dbatabase", user="foo", passwd="bar", port="3306"'''
        test_sqlite3 = """DSN for database 'test' with engine 'django.db.backends.sqlite3':
db.sqlite3"""
        unknown = """DSN for database 'unknown' with engine 'django.db.backends.unknown':
Unknown database, can't generate DSN"""

        call_command("sqldsn", "--all", "--style=all")

        self.assertIn(default_postgresql, m_stdout.getvalue())
        self.assertIn(slave_mysql, m_stdout.getvalue())
        self.assertIn(test_sqlite3, m_stdout.getvalue())
        self.assertIn(unknown, m_stdout.getvalue())
