# -*- coding: utf-8 -*-
"""
    emmett.orm.engines.postgres
    ---------------------------

    Provides ORM PostgreSQL engine specific features.

    :copyright: 2014 Giovanni Barillari
    :license: BSD-3-Clause
"""

from pydal.adapters.postgres import (
    PostgreBoolean,
    PostgrePsycoBoolean,
    PostgrePG8000Boolean
)
from pydal.dialects import register_expression, sqltype_for
from pydal.dialects.postgre import PostgreDialectBooleanJSON
from pydal.helpers.serializers import serializers
from pydal.objects import Expression, Query
from pydal.parsers import for_type as parse_type
from pydal.parsers.postgre import PostgreBooleanAutoJSONParser
from pydal.representers import for_type as repr_type
from pydal.representers.postgre import PostgreArraysRepresenter

from . import adapters


class JSONBPostgreDialect(PostgreDialectBooleanJSON):
    @sqltype_for('jsonb')
    def type_jsonb(self):
        return 'jsonb'

    def _jcontains(self, field, data, query_env={}):
        return '(%s @> %s)' % (
            self.expand(field, query_env=query_env),
            self.expand(data, field.type, query_env=query_env)
        )

    @register_expression('jcontains')
    def _jcontains_expr(self, expr, data):
        return Query(expr.db, self._jcontains, expr, data)

    def _jin(self, field, data, query_env={}):
        return '(%s <@ %s)' % (
            self.expand(field, query_env=query_env),
            self.expand(data, field.type, query_env=query_env)
        )

    @register_expression('jin')
    def _jin_expr(self, expr, data):
        return Query(expr.db, self._jin, expr, data)

    def _jget_common(self, op, field, data, query_env):
        if not isinstance(data, int):
            _dtype = field.type if isinstance(data, (dict, list)) else 'string'
            data = self.expand(data, field_type=_dtype, query_env=query_env)
        return '%s %s %s' % (
            self.expand(field, query_env=query_env),
            op,
            str(data)
        )

    def _jget(self, field, data, query_env={}):
        return self._jget_common('->', field, data, query_env=query_env)

    @register_expression('jget')
    def _jget_expr(self, expr, data):
        return Expression(expr.db, self._jget, expr, data, expr.type)

    def _jgetv(self, field, data, query_env={}):
        return self._jget_common('->>', field, data, query_env=query_env)

    @register_expression('jgetv')
    def _jgetv_expr(self, expr, data):
        return Expression(expr.db, self._jgetv, expr, data, 'string')

    def _jpath(self, field, data, query_env={}):
        return '%s #> %s' % (
            self.expand(field, query_env=query_env),
            self.expand(data, field_type='string', query_env=query_env)
        )

    @register_expression('jpath')
    def _jpath_expr(self, expr, data):
        return Expression(expr.db, self._jpath, expr, data, expr.type)

    def _jpathv(self, field, data, query_env={}):
        return '%s #>> %s' % (
            self.expand(field, query_env=query_env),
            self.expand(data, field_type='string', query_env=query_env)
        )

    @register_expression('jpathv')
    def _jpathv_expr(self, expr, data):
        return Expression(expr.db, self._jpathv, expr, data, 'string')

    def _jhas(self, field, data, all=False, query_env={}):
        _op, _ftype = '?', 'string'
        if isinstance(data, list):
            _op = '?&' if all else '?|'
            _ftype = 'list:string'
        return '%s %s %s' % (
            self.expand(field, query_env=query_env),
            _op,
            self.expand(data, field_type=_ftype, query_env=query_env)
        )

    @register_expression('jhas')
    def _jhas_expr(self, expr, data, all=False):
        return Query(expr.db, self._jhas, expr, data, all=all)


class JSONBPostgreParser(PostgreBooleanAutoJSONParser):
    @parse_type('jsonb')
    def _jsonb(self, value):
        return value


class JSONBPostgreRepresenter(PostgreArraysRepresenter):
    @repr_type('jsonb')
    def _jsonb(self, value):
        return serializers.json(value)


@adapters.register_for('postgres')
class PostgresAdapter(PostgreBoolean):
    def _load_dependencies(self):
        super()._load_dependencies()
        self.dialect = JSONBPostgreDialect(self)
        self.parser = JSONBPostgreParser(self)
        self.representer = JSONBPostgreRepresenter(self)

    def _config_json(self):
        pass

    def _mock_reconnect(self):
        pass


@adapters.register_for('postgres:psycopg2')
class PostgresPsycoPG2Adapter(PostgrePsycoBoolean):
    def _load_dependencies(self):
        super()._load_dependencies()
        self.dialect = JSONBPostgreDialect(self)
        self.parser = JSONBPostgreParser(self)
        self.representer = JSONBPostgreRepresenter(self)

    def _config_json(self):
        pass

    def _mock_reconnect(self):
        pass


@adapters.register_for('postgres:pg8000')
class PostgresPG8000Adapter(PostgrePG8000Boolean):
    def _load_dependencies(self):
        super()._load_dependencies()
        self.dialect = JSONBPostgreDialect(self)
        self.parser = JSONBPostgreParser(self)
        self.representer = JSONBPostgreRepresenter(self)

    def _config_json(self):
        pass

    def _mock_reconnect(self):
        pass
