"""
Tests for the MySQL states
"""

import logging

import pytest

import salt.utils.path
from salt.modules import mysql as mysqlmod
from tests.support.case import ModuleCase
from tests.support.mixins import SaltReturnAssertsMixin

log = logging.getLogger(__name__)

NO_MYSQL = False
try:
    import MySQLdb  # pylint: disable=import-error,unused-import
except ImportError:
    NO_MYSQL = True

if not salt.utils.path.which("mysqladmin"):
    NO_MYSQL = True


@pytest.mark.skipif(
    NO_MYSQL,
    reason="Please install MySQL bindings and a MySQL Server before running "
    "MySQL integration tests.",
)
class MysqlGrantsStateTest(ModuleCase, SaltReturnAssertsMixin):
    """
    Validate the mysql_grants states
    """

    user = "root"
    password = "poney"
    # yep, theses are valid MySQL db names
    # very special chars are _ % and .
    testdb1 = "tes.t'\"saltdb"
    testdb2 = "t_st `(:=salt%b)"
    testdb3 = "test `(:=salteeb)"
    table1 = "foo"
    table2 = "foo `'%_bar"
    users = {
        "user1": {"name": "foo", "pwd": "bar"},
        "user2": {"name": 'user ";--,?:&/\\', "pwd": '";--(),?:@=&/\\'},
        # this is : passwd 標標
        "user3": {"name": "user( @ )=foobar", "pwd": "\xe6\xa8\x99\xe6\xa8\x99"},
        # this is : user/password containing 標標
        "user4": {"name": "user \xe6\xa8\x99", "pwd": "\xe6\xa8\x99\xe6\xa8\x99"},
    }

    @pytest.mark.destructive_test
    def setUp(self):
        """
        Test presence of MySQL server, enforce a root password
        """
        super().setUp()
        NO_MYSQL_SERVER = True
        # now ensure we know the mysql root password
        # one of theses two at least should work
        ret1 = self.run_state(
            "cmd.run",
            name='mysqladmin --host="localhost" -u '
            + self.user
            + ' flush-privileges password "'
            + self.password
            + '"',
        )
        ret2 = self.run_state(
            "cmd.run",
            name='mysqladmin --host="localhost" -u '
            + self.user
            + ' --password="'
            + self.password
            + '" flush-privileges password "'
            + self.password
            + '"',
        )
        key, value = ret2.popitem()
        if value["result"]:
            NO_MYSQL_SERVER = False
        else:
            self.skipTest("No MySQL Server running, or no root access on it.")
        # Create some users and a test db
        for user, userdef in self.users.items():
            self._userCreation(uname=userdef["name"], password=userdef["pwd"])
        self.run_state(
            "mysql_database.present",
            name=self.testdb1,
            character_set="utf8",
            collate="utf8_general_ci",
            connection_user=self.user,
            connection_pass=self.password,
        )
        self.run_state(
            "mysql_database.present",
            name=self.testdb2,
            character_set="utf8",
            collate="utf8_general_ci",
            connection_user=self.user,
            connection_pass=self.password,
        )
        create_query = (
            "CREATE TABLE {tblname} ("
            " id INT NOT NULL AUTO_INCREMENT PRIMARY KEY,"
            " data VARCHAR(100)) ENGINE={engine};".format(
                tblname=mysqlmod.quote_identifier(self.table1),
                engine="MYISAM",
            )
        )
        log.info("Adding table '%s'", self.table1)
        self.run_function(
            "mysql.query",
            database=self.testdb2,
            query=create_query,
            connection_user=self.user,
            connection_pass=self.password,
        )
        create_query = (
            "CREATE TABLE {tblname} ("
            " id INT NOT NULL AUTO_INCREMENT PRIMARY KEY,"
            " data VARCHAR(100)) ENGINE={engine};".format(
                tblname=mysqlmod.quote_identifier(self.table2),
                engine="MYISAM",
            )
        )
        log.info("Adding table '%s'", self.table2)
        self.run_function(
            "mysql.query",
            database=self.testdb2,
            query=create_query,
            connection_user=self.user,
            connection_pass=self.password,
        )

    @pytest.mark.destructive_test
    def tearDown(self):
        """
        Removes created users and db
        """
        for user, userdef in self.users.items():
            self._userRemoval(uname=userdef["name"], password=userdef["pwd"])
        self.run_state(
            "mysql_database.absent",
            name=self.testdb1,
            connection_user=self.user,
            connection_pass=self.password,
        )
        self.run_function(
            "mysql_database.absent",
            name=self.testdb2,
            connection_user=self.user,
            connection_pass=self.password,
        )

    def _userCreation(self, uname, password=None):
        """
        Create a test user
        """
        self.run_state(
            "mysql_user.present",
            name=uname,
            host="localhost",
            password=password,
            connection_user=self.user,
            connection_pass=self.password,
            connection_charset="utf8",
            saltenv={"LC_ALL": "en_US.utf8"},
        )

    def _userRemoval(self, uname, password=None):
        """
        Removes a test user
        """
        self.run_state(
            "mysql_user.absent",
            name=uname,
            host="localhost",
            connection_user=self.user,
            connection_pass=self.password,
            connection_charset="utf8",
            saltenv={"LC_ALL": "en_US.utf8"},
        )

    @pytest.mark.destructive_test
    def test_grant_present_absent(self):
        """
        mysql_database.present
        """
        ret = self.run_state(
            "mysql_grants.present",
            name="grant test 1",
            grant="SELECT, INSERT",
            database=self.testdb1 + ".*",
            user=self.users["user1"]["name"],
            host="localhost",
            grant_option=True,
            revoke_first=True,
            connection_user=self.user,
            connection_pass=self.password,
            connection_charset="utf8",
        )
        self.assertSaltTrueReturn(ret)
        ret = self.run_state(
            "mysql_grants.present",
            name="grant test 2",
            grant="SELECT, ALTER,CREATE TEMPORARY tables, execute",
            database=self.testdb1 + ".*",
            user=self.users["user1"]["name"],
            host="localhost",
            grant_option=True,
            revoke_first=True,
            connection_user=self.user,
            connection_pass=self.password,
            connection_charset="utf8",
        )
        self.assertSaltTrueReturn(ret)
        ret = self.run_state(
            "mysql_grants.present",
            name="grant test 3",
            grant="SELECT, INSERT",
            database=self.testdb2 + "." + self.table2,
            user=self.users["user2"]["name"],
            host="localhost",
            grant_option=True,
            revoke_first=True,
            connection_user=self.user,
            connection_pass=self.password,
            connection_charset="utf8",
        )
        self.assertSaltTrueReturn(ret)
        ret = self.run_state(
            "mysql_grants.present",
            name="grant test 4",
            grant="SELECT, INSERT",
            database=self.testdb2 + "." + self.table2,
            user=self.users["user2"]["name"],
            host="localhost",
            grant_option=True,
            revoke_first=True,
            connection_user=self.user,
            connection_pass=self.password,
            connection_charset="utf8",
        )
        self.assertSaltTrueReturn(ret)
        ret = self.run_state(
            "mysql_grants.present",
            name="grant test 5",
            grant="SELECT, UPDATE",
            database=self.testdb2 + ".*",
            user=self.users["user1"]["name"],
            host="localhost",
            grant_option=True,
            revoke_first=False,
            connection_user=self.user,
            connection_pass=self.password,
            connection_charset="utf8",
        )
        self.assertSaltTrueReturn(ret)
        ret = self.run_state(
            "mysql_grants.absent",
            name="grant test 6",
            grant="SELECT,update",
            database=self.testdb2 + ".*",
            user=self.users["user1"]["name"],
            host="localhost",
            grant_option=True,
            revoke_first=False,
            connection_user=self.user,
            connection_pass=self.password,
            connection_charset="utf8",
        )
        self.assertSaltTrueReturn(ret)
