"""
XX. Generating HTML forms from models

This is mostly just a reworking of the ``form_for_model``/``form_for_instance``
tests to use ``ModelForm``. As such, the text may not make sense in all cases,
and the examples are probably a poor fit for the ``ModelForm`` syntax. In other
words, most of these tests should be rewritten.
"""
from __future__ import unicode_literals

import os
import tempfile

from django.core import validators
from django.core.exceptions import ImproperlyConfigured, ValidationError
from django.core.files.storage import FileSystemStorage
from django.db import models
from django.utils import six
from django.utils.encoding import python_2_unicode_compatible


temp_storage_dir = tempfile.mkdtemp(dir=os.environ['DJANGO_TEST_TEMP_DIR'])
temp_storage = FileSystemStorage(temp_storage_dir)

ARTICLE_STATUS = (
    (1, 'Draft'),
    (2, 'Pending'),
    (3, 'Live'),
)

ARTICLE_STATUS_CHAR = (
    ('d', 'Draft'),
    ('p', 'Pending'),
    ('l', 'Live'),
)


@python_2_unicode_compatible
class Category(models.Model):
    name = models.CharField(max_length=20)
    slug = models.SlugField(max_length=20)
    url = models.CharField('The URL', max_length=40)

    def __str__(self):
        return self.name

    def __repr__(self):
        return self.__str__()


@python_2_unicode_compatible
class Writer(models.Model):
    name = models.CharField(max_length=50, help_text='Use both first and last names.')

    class Meta:
        ordering = ('name',)

    def __str__(self):
        return self.name


@python_2_unicode_compatible
class Article(models.Model):
    headline = models.CharField(max_length=50)
    slug = models.SlugField()
    pub_date = models.DateField()
    created = models.DateField(editable=False)
    writer = models.ForeignKey(Writer)
    article = models.TextField()
    categories = models.ManyToManyField(Category, blank=True)
    status = models.PositiveIntegerField(choices=ARTICLE_STATUS, blank=True, null=True)

    def save(self):
        import datetime
        if not self.id:
            self.created = datetime.date.today()
        return super(Article, self).save()

    def __str__(self):
        return self.headline


class ImprovedArticle(models.Model):
    article = models.OneToOneField(Article)


class ImprovedArticleWithParentLink(models.Model):
    article = models.OneToOneField(Article, parent_link=True)


class BetterWriter(Writer):
    score = models.IntegerField()


@python_2_unicode_compatible
class WriterProfile(models.Model):
    writer = models.OneToOneField(Writer, primary_key=True)
    age = models.PositiveIntegerField()

    def __str__(self):
        return "%s is %s" % (self.writer, self.age)


@python_2_unicode_compatible
class TextFile(models.Model):
    description = models.CharField(max_length=20)
    file = models.FileField(storage=temp_storage, upload_to='tests', max_length=15)

    def __str__(self):
        return self.description

try:
    from django.utils.image import Image  # NOQA: detect if Pillow is installed

    test_images = True

    @python_2_unicode_compatible
    class ImageFile(models.Model):
        def custom_upload_path(self, filename):
            path = self.path or 'tests'
            return '%s/%s' % (path, filename)

        description = models.CharField(max_length=20)

        # Deliberately put the image field *after* the width/height fields to
        # trigger the bug in #10404 with width/height not getting assigned.
        width = models.IntegerField(editable=False)
        height = models.IntegerField(editable=False)
        image = models.ImageField(storage=temp_storage, upload_to=custom_upload_path,
                                  width_field='width', height_field='height')
        path = models.CharField(max_length=16, blank=True, default='')

        def __str__(self):
            return self.description

    @python_2_unicode_compatible
    class OptionalImageFile(models.Model):
        def custom_upload_path(self, filename):
            path = self.path or 'tests'
            return '%s/%s' % (path, filename)

        description = models.CharField(max_length=20)
        image = models.ImageField(storage=temp_storage, upload_to=custom_upload_path,
                                  width_field='width', height_field='height',
                                  blank=True, null=True)
        width = models.IntegerField(editable=False, null=True)
        height = models.IntegerField(editable=False, null=True)
        path = models.CharField(max_length=16, blank=True, default='')

        def __str__(self):
            return self.description
except ImproperlyConfigured:
    test_images = False


@python_2_unicode_compatible
class CommaSeparatedInteger(models.Model):
    field = models.CommaSeparatedIntegerField(max_length=20)

    def __str__(self):
        return self.field


@python_2_unicode_compatible
class Product(models.Model):
    slug = models.SlugField(unique=True)

    def __str__(self):
        return self.slug


@python_2_unicode_compatible
class Price(models.Model):
    price = models.DecimalField(max_digits=10, decimal_places=2)
    quantity = models.PositiveIntegerField()

    def __str__(self):
        return "%s for %s" % (self.quantity, self.price)

    class Meta:
        unique_together = (('price', 'quantity'),)


class ArticleStatus(models.Model):
    status = models.CharField(max_length=2, choices=ARTICLE_STATUS_CHAR, blank=True, null=True)


@python_2_unicode_compatible
class Inventory(models.Model):
    barcode = models.PositiveIntegerField(unique=True)
    parent = models.ForeignKey('self', to_field='barcode', blank=True, null=True)
    name = models.CharField(blank=False, max_length=20)

    class Meta:
        ordering = ('name',)

    def __str__(self):
        return self.name

    def __repr__(self):
        return self.__str__()


class Book(models.Model):
    title = models.CharField(max_length=40)
    author = models.ForeignKey(Writer, blank=True, null=True)
    special_id = models.IntegerField(blank=True, null=True, unique=True)

    class Meta:
        unique_together = ('title', 'author')


class BookXtra(models.Model):
    isbn = models.CharField(max_length=16, unique=True)
    suffix1 = models.IntegerField(blank=True, default=0)
    suffix2 = models.IntegerField(blank=True, default=0)

    class Meta:
        unique_together = (('suffix1', 'suffix2'))
        abstract = True


class DerivedBook(Book, BookXtra):
    pass


@python_2_unicode_compatible
class ExplicitPK(models.Model):
    key = models.CharField(max_length=20, primary_key=True)
    desc = models.CharField(max_length=20, blank=True, unique=True)

    class Meta:
        unique_together = ('key', 'desc')

    def __str__(self):
        return self.key


@python_2_unicode_compatible
class Post(models.Model):
    title = models.CharField(max_length=50, unique_for_date='posted', blank=True)
    slug = models.CharField(max_length=50, unique_for_year='posted', blank=True)
    subtitle = models.CharField(max_length=50, unique_for_month='posted', blank=True)
    posted = models.DateField()

    def __str__(self):
        return self.title


@python_2_unicode_compatible
class DateTimePost(models.Model):
    title = models.CharField(max_length=50, unique_for_date='posted', blank=True)
    slug = models.CharField(max_length=50, unique_for_year='posted', blank=True)
    subtitle = models.CharField(max_length=50, unique_for_month='posted', blank=True)
    posted = models.DateTimeField(editable=False)

    def __str__(self):
        return self.title


class DerivedPost(Post):
    pass


@python_2_unicode_compatible
class BigInt(models.Model):
    biggie = models.BigIntegerField()

    def __str__(self):
        return six.text_type(self.biggie)


class MarkupField(models.CharField):
    def __init__(self, *args, **kwargs):
        kwargs["max_length"] = 20
        super(MarkupField, self).__init__(*args, **kwargs)

    def formfield(self, **kwargs):
        # don't allow this field to be used in form (real use-case might be
        # that you know the markup will always be X, but it is among an app
        # that allows the user to say it could be something else)
        # regressed at r10062
        return None


class CustomFieldForExclusionModel(models.Model):
    name = models.CharField(max_length=10)
    markup = MarkupField()


class FlexibleDatePost(models.Model):
    title = models.CharField(max_length=50, unique_for_date='posted', blank=True)
    slug = models.CharField(max_length=50, unique_for_year='posted', blank=True)
    subtitle = models.CharField(max_length=50, unique_for_month='posted', blank=True)
    posted = models.DateField(blank=True, null=True)


@python_2_unicode_compatible
class Colour(models.Model):
    name = models.CharField(max_length=50)

    def __iter__(self):
        for number in xrange(5):
            yield number

    def __str__(self):
        return self.name


class ColourfulItem(models.Model):
    name = models.CharField(max_length=50)
    colours = models.ManyToManyField(Colour)


class ArticleStatusNote(models.Model):
    name = models.CharField(max_length=20)
    status = models.ManyToManyField(ArticleStatus)


class CustomErrorMessage(models.Model):
    name1 = models.CharField(max_length=50,
        validators=[validators.validate_slug],
        error_messages={'invalid': 'Model custom error message.'})

    name2 = models.CharField(max_length=50,
        validators=[validators.validate_slug],
        error_messages={'invalid': 'Model custom error message.'})

    def clean(self):
        if self.name1 == 'FORBIDDEN_VALUE':
            raise ValidationError({'name1': [ValidationError('Model.clean() error messages.')]})
