import logging

from redash.query_runner import (
    TYPE_BOOLEAN,
    TYPE_DATETIME,
    TYPE_FLOAT,
    TYPE_INTEGER,
    TYPE_STRING,
    BaseQueryRunner,
    register,
)
from redash.utils import json_dumps, json_loads

logger = logging.getLogger(__name__)

try:
    import phoenixdb
    from phoenixdb.errors import Error

    enabled = True

except ImportError:
    enabled = False

TYPES_MAPPING = {
    "VARCHAR": TYPE_STRING,
    "CHAR": TYPE_STRING,
    "BINARY": TYPE_STRING,
    "VARBINARY": TYPE_STRING,
    "BOOLEAN": TYPE_BOOLEAN,
    "TIME": TYPE_DATETIME,
    "DATE": TYPE_DATETIME,
    "TIMESTAMP": TYPE_DATETIME,
    "UNSIGNED_TIME": TYPE_DATETIME,
    "UNSIGNED_DATE": TYPE_DATETIME,
    "UNSIGNED_TIMESTAMP": TYPE_DATETIME,
    "INTEGER": TYPE_INTEGER,
    "UNSIGNED_INT": TYPE_INTEGER,
    "BIGINT": TYPE_INTEGER,
    "UNSIGNED_LONG": TYPE_INTEGER,
    "TINYINT": TYPE_INTEGER,
    "UNSIGNED_TINYINT": TYPE_INTEGER,
    "SMALLINT": TYPE_INTEGER,
    "UNSIGNED_SMALLINT": TYPE_INTEGER,
    "FLOAT": TYPE_FLOAT,
    "UNSIGNED_FLOAT": TYPE_FLOAT,
    "DOUBLE": TYPE_FLOAT,
    "UNSIGNED_DOUBLE": TYPE_FLOAT,
    "DECIMAL": TYPE_FLOAT,
}


class Phoenix(BaseQueryRunner):
    noop_query = "select 1"

    @classmethod
    def configuration_schema(cls):
        return {
            "type": "object",
            "properties": {"url": {"type": "string"}},
            "required": ["url"],
        }

    @classmethod
    def enabled(cls):
        return enabled

    @classmethod
    def type(cls):
        return "phoenix"

    def get_schema(self, get_stats=False):
        schema = {}
        query = """
        SELECT TABLE_SCHEM, TABLE_NAME, COLUMN_NAME
        FROM SYSTEM.CATALOG
        WHERE TABLE_SCHEM IS NULL OR TABLE_SCHEM != 'SYSTEM' AND COLUMN_NAME IS NOT NULL
        """

        results, error = self.run_query(query, None)

        if error is not None:
            self._handle_run_query_error(error)

        results = json_loads(results)

        for row in results["rows"]:
            table_name = "{}.{}".format(row["TABLE_SCHEM"], row["TABLE_NAME"])

            if table_name not in schema:
                schema[table_name] = {"name": table_name, "columns": []}

            schema[table_name]["columns"].append(row["COLUMN_NAME"])

        return list(schema.values())

    def run_query(self, query, user):
        connection = phoenixdb.connect(url=self.configuration.get("url", ""), autocommit=True)

        cursor = connection.cursor()

        try:
            cursor.execute(query)
            column_tuples = [(i[0], TYPES_MAPPING.get(i[1], None)) for i in cursor.description]
            columns = self.fetch_columns(column_tuples)
            rows = [dict(zip(([column["name"] for column in columns]), r)) for i, r in enumerate(cursor.fetchall())]
            data = {"columns": columns, "rows": rows}
            json_data = json_dumps(data)
            error = None
            cursor.close()
        except Error as e:
            json_data = None
            error = "code: {}, sql state:{}, message: {}".format(e.code, e.sqlstate, str(e))
        finally:
            if connection:
                connection.close()

        return json_data, error


register(Phoenix)
