# -*- coding: utf-8 -*-
"""
JSONField automatically serializes most Python terms to JSON data.
Creates a TEXT field with a default value of "{}".  See test_json.py for
more information.

 from django.db import models
 from django_extensions.db.fields import json

 class LOL(models.Model):
     extra = json.JSONField()
"""

import datetime
import json

from bson.objectid import ObjectId
from decimal import Decimal
from django.conf import settings
from django.db import models

from events.common_app.jsonfield.fields import JSONFieldBase, JSONFormField


class JSONEncoder(json.JSONEncoder):
    def default(self, obj):
        if isinstance(obj, Decimal):
            return str(obj)
        elif isinstance(obj, datetime.datetime):
            assert settings.TIME_ZONE == 'UTC'
            return obj.strftime('%Y-%m-%dT%H:%M:%SZ')
        return json.JSONEncoder.default(self, obj)


def dumps(value):
    return JSONEncoder().encode(value)


def loads(txt):
    return json.loads(txt, parse_float=Decimal)


class JSONDict(dict):
    """
    Hack so repr() called by dumpdata will output JSON instead of
    Python formatted data.  This way fixtures will work!
    """
    def __repr__(self):
        return dumps(self)


class JSONList(list):
    """
    As above
    """
    def __repr__(self):
        return dumps(self)


class JSONField(models.TextField):
    """JSONField is a generic textfield that neatly serializes/unserializes
    JSON objects seamlessly.  Main thingy must be a dict object."""

    def __init__(self, *args, **kwargs):
        if 'default' not in kwargs:
            kwargs['default'] = '{}'
        super(JSONField, self).__init__(*args, **kwargs)

    def to_python(self, value):
        """Convert our string value to JSON after we load it from the DB"""
        if not value:
            return {}
        elif isinstance(value, str):
            res = loads(value)
            if isinstance(res, dict):
                return JSONDict(**res)
            else:
                return JSONList(res)

        else:
            return value

    # def get_db_prep_save(self, value, connection):
    #     """Convert our JSON object to a string before we save"""
    #     if not isinstance(value, (list, dict)):
    #         return super(JSONField, self).get_db_prep_save("", connection)
    #     else:
    #         return super(JSONField, self).get_db_prep_save(dumps(value),
    #                                                        connection)

    def get_prep_value(self, value):
        """Convert our JSON object to a string before we save"""
        value = super(JSONField, self).get_prep_value(value)
        if not isinstance(value, (list, dict)):
            return ''
        else:
            return dumps(value)

    def from_db_value(self, value, expression, connection, context):
        return self.to_python(value)


class ReturnSeparatedList(list):
    """
    As above
    """
    def __repr__(self):
        # чтобы правильно отображалось в форме
        return '\n'.join(self)


class ReturnSeparatedListField(models.TextField):
    # todo: test me
    def __init__(self, *args, **kwargs):
        if 'default' not in kwargs:
            kwargs['default'] = ''
        super(ReturnSeparatedListField, self).__init__(*args, **kwargs)

    def to_python(self, value):
        if not value:
            return ReturnSeparatedList([])
        elif isinstance(value, str):
            return ReturnSeparatedList([i.strip() for i in value.split('\n') if i.strip()])
        else:
            return value

    def get_prep_value(self, value):
        """Convert our JSON object to a string before we save"""
        value = super(ReturnSeparatedListField, self).get_prep_value(value)
        if isinstance(value, list):
            value = '\n'.join(value)
        return value

    def value_to_string(self, obj):
        value = self.value_from_object(obj)
        return self.get_prep_value(value)

    def from_db_value(self, value, expression, connection, context):
        return self.to_python(value)


class BigIntPostgresField(models.IntegerField):
    def db_type(self, connection):
        return 'bigint'


class SimpleTextField(models.TextField):
    def db_type(self, connection):
        return 'text'


class TinyTextField(models.TextField):
    def db_type(self, connection):
        return 'tinytext'


class JSONFieldForSimpleText(JSONFieldBase, models.TextField):
    """JSONField is a generic textfield that serializes/unserializes JSON objects"""
    form_class = JSONFormField

    def db_type(self, connection):
        return 'text'


class JSONFieldForTinyText(JSONFieldBase, models.TextField):
    """JSONField is a generic textfield that serializes/unserializes JSON objects"""
    form_class = JSONFormField

    def db_type(self, connection):
        return 'tinytext'


if settings.USE_HEX_IDENTIFIER_FIELD:
    class IdentifierField(models.CharField):
        def __init__(self, *args, **kwargs):
            kwargs['max_length'] = 24
            super(IdentifierField, self).__init__(*args, **kwargs)

    class AutoIdentifierField(IdentifierField):
        @staticmethod
        def get_object_id():
            return str(ObjectId())

        def __init__(self, *args, **kwargs):
            kwargs['primary_key'] = True
            kwargs['default'] = AutoIdentifierField.get_object_id
            super(AutoIdentifierField, self).__init__(*args, **kwargs)
else:
    class IdentifierField(models.PositiveIntegerField):
        pass

    class AutoIdentifierField(models.AutoField):
        pass
