"""This module provides SQL Server specific fields for Django models."""
from __future__ import unicode_literals
import datetime
from django.db import models
from django.forms import ValidationError
from django.utils import six, timezone
from django.utils.translation import ugettext_lazy as _

if six.PY3:
    long = int

__all__ = (
    'BigAutoField',
    'BigForeignKey',
    'BigIntegerField',
    'DateField',
    'DateTimeField',
    'DateTimeOffsetField',
    'LegacyTimeField',
    'LegacyDateField',
    'LegacyDateTimeField',
    'TimeField',
)


class BigAutoField(models.AutoField):
    """A bigint IDENTITY field"""
    def get_internal_type(self):
        return "BigAutoField"

    def to_python(self, value):
        if value is None:
            return value
        try:
            return long(value)
        except (TypeError, ValueError):
            raise ValidationError(
                _("This value must be a long."))

    def get_db_prep_value(self, value, connection=None, prepared=False):
        if value is None:
            return None
        return long(value)


class BigForeignKey(models.ForeignKey):
    """A ForeignKey field that points to a BigAutoField or BigIntegerField"""
    def db_type(self, connection=None):
        try:
            return models.BigIntegerField().db_type(connection=connection)
        except AttributeError:
            return models.BigIntegerField().db_type()

BigIntegerField = models.BigIntegerField


def convert_microsoft_date_to_isoformat(value):
    if isinstance(value, six.string_types):
        value = value.replace(' +', '+').replace(' -', '-')
    return value


class DateField(models.DateField):
    """
    A DateField backed by a 'date' database field.
    """
    def get_internal_type(self):
        return 'NewDateField'

    def to_python(self, value):
        val = super(DateField, self).to_python(
            convert_microsoft_date_to_isoformat(value)
        )
        if isinstance(val, datetime.datetime):
            val = datetime.date()
        return val


class DateTimeField(models.DateTimeField):
    """
    A DateTimeField backed by a 'datetime2' database field.
    """
    def get_internal_type(self):
        return 'NewDateTimeField'

    def to_python(self, value):
        from django.conf import settings
        result = super(DateTimeField, self).to_python(
            convert_microsoft_date_to_isoformat(value)
        )
        if result:
            if timezone.is_aware(result) and not settings.USE_TZ:
                result = timezone.make_naive(result, timezone.utc)
            elif settings.USE_TZ:
                result = timezone.make_aware(result, timezone.utc)
        return result

    def get_db_prep_value(self, value, connection, prepared=False):
        if not prepared:
            value = self.get_prep_value(value)
        return connection.ops._new_value_to_db_datetime(value)


class DateTimeOffsetField(models.DateTimeField):
    """
    A DateTimeOffsetField backed by a 'datetimeoffset' database field.
    """
    def get_internal_type(self):
        return 'DateTimeOffsetField'

    def to_python(self, value):
        return super(DateTimeOffsetField, self).to_python(
            convert_microsoft_date_to_isoformat(value)
        )

    def get_db_prep_value(self, value, connection, prepared=False):
        if not prepared:
            value = self.get_prep_value(value)
        if value is None:
            return None
        return value.isoformat()


class TimeField(models.TimeField):
    """
    A TimeField backed by a 'time' database field.
    """
    def get_internal_type(self):
        return 'NewTimeField'

    def to_python(self, value):
        return super(TimeField, self).to_python(
            convert_microsoft_date_to_isoformat(value)
        )

    def get_db_prep_value(self, value, connection, prepared=False):
        if not prepared:
            value = self.get_prep_value(value)
        return connection.ops._new_value_to_db_time(value)


class LegacyDateField(models.DateField):
    """
    A DateField that is backed by a 'datetime' database field.
    """
    def get_internal_type(self):
        return 'LegacyDateField'

    def to_python(self, value):
        val = super(LegacyDateField, self).to_python(
            convert_microsoft_date_to_isoformat(value)
        )
        if isinstance(val, datetime.datetime):
            val = datetime.date()
        return val


class LegacyDateTimeField(models.DateTimeField):
    """
    A DateTimeField that is backed by a 'datetime' database field.
    """
    def get_internal_type(self):
        return 'LegacyDateTimeField'

    def to_python(self, value):
        return super(LegacyDateTimeField, self).to_python(
            convert_microsoft_date_to_isoformat(value)
        )

    def get_db_prep_value(self, value, connection, prepared=False):
        if not prepared:
            value = self.get_prep_value(value)
        return connection.ops._legacy_value_to_db_datetime(value)


class LegacyTimeField(models.TimeField):
    """
    A TimeField that is backed by a 'datetime' database field.
    """
    def get_internal_type(self):
        return 'LegacyTimeField'

    def to_python(self, value):
        val = super(LegacyTimeField, self).to_python(
            convert_microsoft_date_to_isoformat(value)
        )
        if isinstance(val, datetime.datetime):
            val = val.time()
        return val

    def get_db_prep_value(self, value, connection, prepared=False):
        if not prepared:
            value = self.get_prep_value(value)
        return connection.ops._legacy_value_to_db_time(value)
