# -*- coding: utf-8 -*-

from ...support.grammar import Grammar
from ...support.fluent import Fluent
from ...query.expression import QueryExpression
from ...dbal.column import Column
from ...dbal.table_diff import TableDiff
from ...dbal.comparator import Comparator
from ..blueprint import Blueprint


class SchemaGrammar(Grammar):
    def __init__(self, connection):
        super(SchemaGrammar, self).__init__(marker=connection.get_marker())

        self._connection = connection

    def compile_rename_column(self, blueprint, command, connection):
        """
        Compile a rename column command.

        :param blueprint: The blueprint
        :type blueprint: Blueprint

        :param command: The command
        :type command: Fluent

        :param connection: The connection
        :type connection: orator.connections.Connection

        :rtype: list
        """
        schema = connection.get_schema_manager()

        table = self.get_table_prefix() + blueprint.get_table()

        column = connection.get_column(table, command.from_)

        table_diff = self._get_renamed_diff(blueprint, command, column, schema)

        return schema.get_database_platform().get_alter_table_sql(table_diff)

    def _get_renamed_diff(self, blueprint, command, column, schema):
        """
        Get a new column instance with the new column name.

        :param blueprint: The blueprint
        :type blueprint: Blueprint

        :param command: The command
        :type command: Fluent

        :param column: The column
        :type column: orator.dbal.Column

        :param schema: The schema
        :type schema: orator.dbal.SchemaManager

        :rtype: orator.dbal.TableDiff
        """
        table_diff = self._get_table_diff(blueprint, schema)

        return self._set_renamed_columns(table_diff, command, column)

    def _set_renamed_columns(self, table_diff, command, column):
        """
        Set the renamed columns on the table diff.

        :rtype: orator.dbal.TableDiff
        """
        new_column = Column(command.to, column.get_type(), column.to_dict())

        table_diff.renamed_columns = {command.from_: new_column}

        return table_diff

    def compile_foreign(self, blueprint, command, _):
        """
        Compile a foreign key command.

        :param blueprint: The blueprint
        :type blueprint: Blueprint

        :param command: The command
        :type command: Fluent

        :rtype: str
        """
        table = self.wrap_table(blueprint)

        on = self.wrap_table(command.on)

        columns = self.columnize(command.columns)

        on_columns = self.columnize(
            command.references
            if isinstance(command.references, list)
            else [command.references]
        )

        sql = "ALTER TABLE %s ADD CONSTRAINT %s " % (table, command.index)

        sql += "FOREIGN KEY (%s) REFERENCES %s (%s)" % (columns, on, on_columns)

        if command.get("on_delete"):
            sql += " ON DELETE %s" % command.on_delete

        if command.get("on_update"):
            sql += " ON UPDATE %s" % command.on_update

        return sql

    def _get_columns(self, blueprint):
        """
        Get the blueprint's columns definitions.

        :param blueprint: The blueprint
        :type blueprint: Blueprint

        :rtype: list
        """
        columns = []

        for column in blueprint.get_added_columns():
            sql = self.wrap(column) + " " + self._get_type(column)

            columns.append(self._add_modifiers(sql, blueprint, column))

        return columns

    def _add_modifiers(self, sql, blueprint, column):
        """
        Add the column modifiers to the deifinition
        """
        for modifier in self._modifiers:
            method = "_modify_%s" % modifier

            if hasattr(self, method):
                sql += getattr(self, method)(blueprint, column)

        return sql

    def _get_command_by_name(self, blueprint, name):
        """
        Get the primary key command it it exists.
        """
        commands = self._get_commands_by_name(blueprint, name)

        if len(commands):
            return commands[0]

    def _get_commands_by_name(self, blueprint, name):
        """
        Get all of the commands with a given name.
        """
        return list(filter(lambda value: value.name == name, blueprint.get_commands()))

    def _get_type(self, column):
        """
        Get the SQL for the column data type.