from hashlib import sha256

from django.core.exceptions import *
from django.core.validators import RegexValidator
from django.db import models
from django.db.models import Q

from account.models import SiteUser
from datetime import datetime
import os

from PIL import Image


class City(models.Model):
    code = models.CharField(primary_key=True, max_length=20, verbose_name="Код города", validators=[
        RegexValidator(regex="^[0-9a-zA-Z_]*$"),
    ])
    name = models.CharField(unique=True, max_length=50, verbose_name="Название города")

    def __str__(self):
        return f"{self.name} ({self.code})"

    @staticmethod
    def to_choices():
        return City.objects.order_by('name').values_list('code', 'name')


def _phone_validate(phone, order=None):
    if phone is not None:
        err_obj = {"phone": f'Телефон {phone} уже используется другим аккаунтом'}
        if SiteUser.objects.filter(phone=phone).count() > 0:
            raise ValidationError(
                err_obj,
                code='permission'
            )
        if order is None:
            if Order.objects.filter(phone=phone).count() > 0:
                raise ValidationError(
                    err_obj,
                    code='permission'
                )
        else:
            if Order.objects.filter(~Q(id=order.id), phone=phone).count() > 0:
                raise ValidationError(
                    err_obj,
                    code='permission'
                )


def _email_validate(email, order=None):
    if email is not None:
        err_obj = {'email': f'Почта {email} уже используется другим аккаунтом'}
        if SiteUser.objects.filter(email=email).count():
            raise ValidationError(
                err_obj,
                code='permission'
            )
        if order is None:
            if Order.objects.filter(email=email).count() > 0:
                raise ValidationError(
                    err_obj,
                    code='permission'
                )
        else:
            if Order.objects.filter(~Q(id=order.id), email=email).count() > 0:
                raise ValidationError(
                    err_obj,
                    code='permission'
                )


class Order(models.Model):
    # основные поля: название и описание
    name = models.CharField(max_length=200, verbose_name="Название заказа")
    description = models.TextField(blank=True, verbose_name="Описание")

    # площадь в квадратных метрах
    square = models.DecimalField(max_digits=7, decimal_places=2, blank=False, verbose_name="Площадь в м²")

    work_time = models.CharField(max_length=100, blank=True, verbose_name="Рабочее время")

    # дальше вид дома, тип ремонта, тип квартиры, требуется дизайн проект, закуп материала, тип исполнителя

    CHOICE_UNDEFINED = ''

    # тип ремонта
    TYPE_OF_RENOVATION_OVERHAUL = 'overhaul'
    TYPE_OF_RENOVATION_PARTIAL = 'partial'
    TYPE_OF_RENOVATION_REDECOR = 'redecor'
    TYPE_OF_RENOVATION_PREMIUM = 'premium'
    TYPE_OF_RENOVATION_DESIGN = 'design'

    TYPE_OF_RENOVATION_CHOICES = [
        (TYPE_OF_RENOVATION_OVERHAUL, 'Капитальный'),
        (TYPE_OF_RENOVATION_PARTIAL, 'Частичный'),
        (TYPE_OF_RENOVATION_REDECOR, 'Косметический'),
        (TYPE_OF_RENOVATION_PREMIUM, 'Премиальный'),
        (TYPE_OF_RENOVATION_DESIGN, 'Дизайнерский'),
    ]
    type_of_renovation = models.CharField(max_length=10, choices=TYPE_OF_RENOVATION_CHOICES, default=CHOICE_UNDEFINED,
                                          blank=True, verbose_name="Тип ремонта")

    # тип дома
    TYPE_OF_HOUSE_BLOCK = 'block'
    TYPE_OF_HOUSE_BRICK = 'brick'
    TYPE_OF_HOUSE_MONOLITH = 'monolith'
    TYPE_OF_HOUSE_PANEL = 'panel'

    TYPE_OF_HOUSE_CHOICES = [
        (TYPE_OF_HOUSE_BLOCK, 'Блочный'),
        (TYPE_OF_HOUSE_BRICK, 'Кирпичный'),
        (TYPE_OF_HOUSE_MONOLITH, 'Монолит'),
        (TYPE_OF_HOUSE_PANEL, 'Панельный'),
    ]
    type_of_house = models.CharField(max_length=10, choices=TYPE_OF_HOUSE_CHOICES, blank=True, default=CHOICE_UNDEFINED,
                                     verbose_name="Тип дома")

    # тип квартиры
    TYPE_OF_ROOM_PRIMARY = 'primary'
    TYPE_OF_ROOM_SECONDARY = 'secondary'

    TYPE_OF_ROOM_CHOICES = [
        (TYPE_OF_ROOM_PRIMARY, 'Первичка'),
        (TYPE_OF_ROOM_SECONDARY, 'Вторичка')
    ]
    type_of_room = models.CharField(max_length=10, choices=TYPE_OF_ROOM_CHOICES, blank=True, default=CHOICE_UNDEFINED,
                                    verbose_name="Тип квартиры")

    # требуется дизайн проект
    REQUIRED_DESIGN_CHOICES = ((True, 'Да'), (False, 'Нет'))
    is_require_design = models.BooleanField(default=None, blank=True, null=True, choices=REQUIRED_DESIGN_CHOICES,
                                            verbose_name="Требуется дизайн проект")

    # закуп материала
    PURCHASE_OF_MATERIAL_EXECUTOR = 'executor'
    PURCHASE_OF_MATERIAL_CUSTOMER = 'customer'

    PURCHASE_OF_MATERIAL_CHOICES = [
        (PURCHASE_OF_MATERIAL_EXECUTOR, 'Исполнитель'),
        (PURCHASE_OF_MATERIAL_CUSTOMER, 'Заказчик')
    ]
    purchase_of_material = models.CharField(max_length=10, choices=PURCHASE_OF_MATERIAL_CHOICES,
                                            blank=True, default=CHOICE_UNDEFINED, verbose_name="Закуп материала")

    # тип исполнителя
    TYPE_OF_EXECUTOR_INDIVIDUAL = 'individual'
    TYPE_OF_EXECUTOR_COMPANY = 'company'

    TYPE_OF_EXECUTOR_CHOICES = [
        (TYPE_OF_EXECUTOR_INDIVIDUAL, 'Самозанятый/бригада'),
        (TYPE_OF_EXECUTOR_COMPANY, 'Компания')
    ]
    type_of_executor = models.CharField(max_length=10, choices=TYPE_OF_EXECUTOR_CHOICES,
                                        blank=True, default=CHOICE_UNDEFINED, verbose_name="Тип исполнителя")

    # дальше отдельные параметры
    is_with_warranty = models.BooleanField(default=True, verbose_name="С гарантией")
    is_with_contract = models.BooleanField(default=False, verbose_name="Работа по договору")
    is_with_trade = models.BooleanField(default=False, verbose_name="Возможен торг")
    is_with_cleaning = models.BooleanField(default=False, verbose_name="С уборкой")
    is_with_garbage_removal = models.BooleanField(default=False, verbose_name="С вывозом мусора")

    # примерная цена
    approximate_price = models.DecimalField(max_digits=12, decimal_places=2, blank=False, verbose_name="Цена")

    date_start = models.DateField(null=True, blank=True, default=None, verbose_name="Дата начала")
    date_end = models.DateField(null=True, blank=True, default=None, verbose_name="Дата окончания")

    address_city = models.ForeignKey(City, on_delete=models.CASCADE, blank=False, related_name="address_city",
                                     verbose_name="Город")
    address_text = models.CharField(max_length=70, blank=True, verbose_name="Улица, дом")

    owner = models.ForeignKey(SiteUser, on_delete=models.CASCADE, blank=True, null=True, related_name="owner",
                              verbose_name="Владелец")

    email = models.EmailField(null=True, blank=True, verbose_name="Email")
    phone = models.CharField(null=True, blank=True, max_length=16, verbose_name="Телефон", validators=[
        RegexValidator(regex="^\\+7[0-9]{10}$")
    ])

    create_time = models.DateTimeField(default=datetime.now, editable=False, verbose_name="Время создания")

    moderated = models.BooleanField(default=True, verbose_name="Модерирован")
    published = models.BooleanField(default=True, verbose_name="Опубликован")

    def clean(self):
        errors = {}
        try:
            if self.pk is None:
                _phone_validate(self.phone)
            else:
                _phone_validate(self.phone, self)
        except ValidationError as e:
            errors["phone"] = [e]

        try:
            if self.pk is None:
                _email_validate(self.email)
            else:
                _email_validate(self.email, self)
        except ValidationError as e:
            errors["email"] = [e]

        if len(errors) > 0:
            raise ValidationError(errors)

    def save(self, *args, **kwargs):
        if self.pk is None:
            if self.owner is None:
                if self.phone is None or self.email is None:
                    raise Exception("Could not save: need contact information")

        if self.owner is not None:
            self.phone = None
            self.email = None

        self.full_clean()

        super().save(*args, **kwargs)

    def __str__(self):
        return self.name

    @staticmethod
    def get_all_for_user(user):
        if user.is_staff:
            return Order.objects.filter().order_by('create_time')
        else:
            return Order.objects.filter(published=True, moderated=True).order_by('create_time')

    @staticmethod
    def get_for_user_by_id(user, order_id):
        q = Order.get_all_for_user(user).filter(id=order_id)
        if len(q) == 0:
            return None
        else:
            return q[0]


def _upload_image_filename(instance, filename):
    name, ext = os.path.splitext(filename)
    fn = sha256((str(datetime.now()) + name).encode('utf-8')).hexdigest() + ext
    return "order-images/" + fn


# TOFIX сделать удаление ненужных картинок из файловой системы

class OrderImage(models.Model):
    MAX_IMAGES = 10

    order = models.ForeignKey(Order, on_delete=models.CASCADE, related_name="order", verbose_name="Заказ")
    image = models.ImageField(upload_to=_upload_image_filename, verbose_name="Картинка")

    def __str__(self):
        return f"{self.id}: {self.order}"

    def save(self, *args, **kwargs):
        super(OrderImage, self).save(*args, **kwargs)
        img = Image.open(self.image.path)

        if img.width > 1920 or img.height > 1080:
            # нужно урезать фортку так, чтобы пропорции уменьшились пропорционально
            output_size = (1920, 1080)
            img.thumbnail(output_size)
            img.save(self.image.path)


class OrderRespond(models.Model):
    create_time = models.DateTimeField(default=datetime.now, editable=False, verbose_name="Время отклика")
    user = models.ForeignKey(SiteUser, on_delete=models.CASCADE, related_name="respond_user", verbose_name="Пользователь")
    order = models.ForeignKey(Order, on_delete=models.CASCADE, related_name="respond_order", verbose_name="Заказ")

    class Meta:
        constraints = [
            models.UniqueConstraint(
                fields=['user', 'order'], name='unique_order_respond_user_order'
            )
        ]

    def __str__(self):
        return f"{self.order}: {self.user}"

    def save(self, *args, **kwargs):
        if Order.objects.get(id=self.order.id) == self.user.id:
            raise Exception("User can't respond to self order")

        self.full_clean()

        super().save(*args, **kwargs)

