"""
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.IntegerField):
    """
    A field used to store currency amounts in cents (etc) as per stripe.
    """

    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."""
        defaults = {"decimal_places": 2, "max_digits": 8}
        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
