"""
dj-stripe Custom Field Definitions
"""
import decimal

from django.core.validators import MaxValueValidator, MinValueValidator
from django.db import models

from .settings import USE_NATIVE_JSONFIELD
from .utils import convert_tstamp

if USE_NATIVE_JSONFIELD:
    from django.contrib.postgres.fields import JSONField as BaseJSONField
else:
    from jsonfield import JSONField as BaseJSONField


class PaymentMethodForeignKey(models.ForeignKey):
    def __init__(self, **kwargs):
        kwargs.setdefault("to", "DjstripePaymentMethod")
        super().__init__(**kwargs)


class StripePercentField(models.DecimalField):
    """A field used to define a percent according to djstripe logic."""

    def __init__(self, *args, **kwargs):
        """Assign default args to this field."""
        defaults = {
            "decimal_places": 2,
            "max_digits": 5,
            "validators": [MinValueValidator(1), MaxValueValidator(100)],
        }
        defaults.update(kwargs)
        super().__init__(*args, **defaults)


class StripeCurrencyCodeField(models.CharField):
    """
    A field used to store a three-letter currency code (eg. usd, eur, ...)
    """

    def __init__(self, *args, **kwargs):
        defaults = {"max_length": 3, "help_text": "Three-letter ISO currency code"}
        defaults.update(kwargs)
        super().__init__(*args, **defaults)


class StripeQuantumCurrencyAmountField(models.BigIntegerField):
    """
    A field used to store currency amounts in cents (etc) as per stripe.
    By contacting stripe support, some accounts will have their limit raised to 11
    digits, hence the use of BigIntegerField instead of IntegerField
    """

    pass


class StripeDecimalCurrencyAmountField(models.DecimalField):
    """
    A legacy field to store currency amounts in dollars (etc).

    Stripe is always in cents. Historically djstripe stored everything in dollars.

    Note: Don't use this for new fields, use StripeQuantumCurrencyAmountField instead.
    We're planning on migrating existing fields in dj-stripe 3.0,
    see https://github.com/dj-stripe/dj-stripe/issues/955
    """

    def __init__(self, *args, **kwargs):
        """
        Assign default args to this field. By contacting stripe support, some accounts
        will have their limit raised to 11 digits
        """
        defaults = {"decimal_places": 2, "max_digits": 11}
        defaults.update(kwargs)
        super().__init__(*args, **defaults)

    def stripe_to_db(self, data):
        """Convert the raw value to decimal representation."""
        val = data.get(self.name)

        # Note: 0 is a possible return value, which is 'falseish'
        if val is not None:
            return val / decimal.Decimal("100")


class StripeEnumField(models.CharField):
    def __init__(self, enum, *args, **kwargs):
        self.enum = enum
        choices = enum.choices
        defaults = {"choices": choices, "max_length": max(len(k) for k, v in choices)}
        defaults.update(kwargs)
        super().__init__(*args, **defaults)

    def deconstruct(self):
        name, path, args, kwargs = super().deconstruct()
        kwargs["enum"] = self.enum
        del kwargs["choices"]
        return name, path, args, kwargs


class StripeIdField(models.CharField):
    """A field with enough space to hold any stripe ID."""

    def __init__(self, *args, **kwargs):
        """
        Assign default args to this field.

        As per: https://stripe.com/docs/upgrades
        You can safely assume object IDs we generate will never exceed 255
        characters, but you should be able to handle IDs of up to that
        length.
        """
        defaults = {"max_length": 255, "blank": False, "null": False}
        defaults.update(kwargs)
        super().__init__(*args, **defaults)


class StripeDateTimeField(models.DateTimeField):
    """A field used to define a DateTimeField value according to djstripe logic."""

    def stripe_to_db(self, data):
        """Convert the raw timestamp value to a DateTime representation."""
        val = data.get(self.name)

        # Note: 0 is a possible return value, which is 'falseish'
        if val is not None:
            return convert_tstamp(val)


class JSONField(BaseJSONField):
    """A field used to define a JSONField value according to djstripe logic."""

    pass
