from django.db import models
from django.urls import reverse
from django.utils import timezone

# Create your models here.
to_remove = ['id', '_state']


def _is_changed(entity, history):
    if history is None:
        return True
    if getattr(entity, '__module__') == models.__name__ or getattr(history, '__module__') == models.__name__:
        raise Exception('entities not in fkrdjan.models')
    fields = [x for x in list(vars(entity)) if x not in to_remove]
    for field in fields:
        if str(getattr(entity, field)) != str(getattr(history, field)):
            return True
    return False


class MetropolitanArea(models.Model):
    full_name = models.CharField(max_length=200)
    short_name = models.CharField(max_length=200)
    oktmo = models.CharField(max_length=20)

    def save(self, *args, **kwargs):
        super(MetropolitanArea, self).save(*args, **kwargs)
        history = MetropolitanAreaHistory.objects.filter(
            metropolitan_area_id=self.id).order_by('-revision_date').first()
        if _is_changed(self, history):
            MetropolitanAreaHistory.objects.create(
                full_name=self.full_name,
                short_name=self.short_name,
                oktmo=self.oktmo,
                metropolitan_area=self)

    def __str__(self):
        return self.short_name

    def get_absolute_url(self):
        return reverse('fkrdjan:metroarea_detail', args=(self.id,))


class MetropolitanAreaHistory(models.Model):
    full_name = models.CharField(max_length=200)
    short_name = models.CharField(max_length=200)
    oktmo = models.CharField(max_length=20)

    metropolitan_area = models.ForeignKey(
        MetropolitanArea, on_delete=models.PROTECT)
    revision_date = models.DateTimeField()

    def save(self, *args, **kwargs):
        self.revision_date = timezone.now()
        super(MetropolitanAreaHistory, self).save(*args, **kwargs)

    def __str__(self):
        return f'{self.short_name}'

    def get_absolute_url(self):
        return reverse('fkrdjan:metroarea_history_detail', args=(self.id,))


class Contractor(models.Model):
    full_name = models.CharField(max_length=200)
    short_name = models.CharField(max_length=200, null=True)
    inn = models.CharField(max_length=20)
    ogrn = models.CharField(max_length=20)
    kpp = models.CharField(max_length=20)
    bik = models.CharField(max_length=20)
    bill_number = models.CharField(max_length=30)
    system_no = models.BooleanField()
    year_of_last_change = models.IntegerField(null=True)

    def save(self, *args, **kwargs):
        super(Contractor, self).save(*args, **kwargs)
        history = ContractorHistory.objects.filter(
            contractor_id=self.id).order_by('-revision_date').first()
        if _is_changed(self, history):
            ContractorHistory.objects.create(
                short_name=self.short_name,
                full_name=self.full_name,
                inn=self.inn,
                ogrn=self.ogrn,
                kpp=self.kpp,
                bik=self.bik,
                bill_number=self.bill_number,
                system_no=self.system_no,
                year_of_last_change=self.year_of_last_change,
                contractor=self)

    def __str__(self):
        return self.full_name

    def get_absolute_url(self):
        return reverse('fkrdjan:contractor_detail', args=(self.id,))


class ContractorHistory(models.Model):
    full_name = models.CharField(max_length=200)
    short_name = models.CharField(max_length=200, null=True)
    inn = models.CharField(max_length=20)
    ogrn = models.CharField(max_length=20)
    kpp = models.CharField(max_length=20)
    bik = models.CharField(max_length=20)
    bill_number = models.CharField(max_length=30)
    system_no = models.BooleanField()
    year_of_last_change = models.IntegerField(null=True)

    contractor = models.ForeignKey(Contractor, on_delete=models.PROTECT)
    revision_date = models.DateTimeField()

    def save(self, *args, **kwargs):
        self.revision_date = timezone.now()
        super(ContractorHistory, self).save(*args, **kwargs)

    def __str__(self):
        return self.full_name

    def get_absolute_url(self):
        return reverse('fkrdjan:contractor_history_detail', args=(self.id,))


class TechnicalCustomer(models.Model):
    full_name = models.CharField(max_length=200)
    mail = models.CharField(max_length=200, null=True)
    metropolitan_area = models.ForeignKey(
        MetropolitanArea, on_delete=models.PROTECT)

    def save(self, *args, **kwargs):
        super(TechnicalCustomer, self).save(*args, **kwargs)
        history = TechnicalCustomerHistory.objects.filter(
            technical_customer_id=self.id).order_by('-revision_date').first()
        if _is_changed(self, history):
            TechnicalCustomerHistory.objects.create(
                full_name=self.full_name,
                mail=self.mail,
                metropolitan_area=self.metropolitan_area,
                technical_customer=self
            )

    def __str__(self):
        return self.full_name

    def get_absolute_url(self):
        return reverse('fkrdjan:techcustomer_detail', args=(self.id,))


class TechnicalCustomerHistory(models.Model):
    full_name = models.CharField(max_length=200)
    mail = models.CharField(max_length=200, null=True)
    metropolitan_area = models.ForeignKey(
        MetropolitanArea, on_delete=models.PROTECT)

    technical_customer = models.ForeignKey(
        TechnicalCustomer, on_delete=models.PROTECT)
    revision_date = models.DateTimeField()

    def save(self, *args, **kwargs):
        self.revision_date = timezone.now()
        super(TechnicalCustomerHistory, self).save(*args, **kwargs)

    def __str__(self):
        return self.full_name

    def get_absolute_url(self):
        return reverse('fkrdjan:techcustomer_history_detail', args=(self.id,))


class StrElementGroup(models.Model):
    name = models.CharField(max_length=200)


class Unit(models.Model):
    name = models.CharField(max_length=200)
    is_actual = models.BooleanField()


class StrElementCharacteristics(models.Model):
    name = models.CharField(max_length=200)
    is_actual = models.BooleanField()


class Work(models.Model):
    name = models.CharField(max_length=200)
    is_actual = models.BooleanField()
    fkr_id = models.IntegerField()


class LinkStrElementWork(models.Model):
    str_element = models.ForeignKey(
        StructuralElement, on_delete=models.PROTECT)
    work = models.ForeignKey(Work, on_delete=models.PROTECT)


class StructuralElement(models.Model):
    name = models.CharField(max_length=200)
    is_actual = models.BooleanField()
    unit = models.ForeignKey(Unit, on_delete=models.PROTECT)
    marginal_cost = models.DecimalField(max_digits=10, decimal_places=2)
    marginal_cost_unique = models.DecimalField(max_digits=10, decimal_places=2)
    str_element_characteristics = models.ForeignKey(
        StrElementCharacteristics, on_delete=models.PROTECT)
    str_element_group = models.ForeignKey(
        StructuralElementGroup, on_delete=models.PROTECT)

    def save(self, *args, **kwargs):
        super(StructuralElement, self).save(*args, **kwargs)
        history = StructuralElementHistory.objects.filter(
            structural_element_id=self.id).order_by('-revision_date').first()
        if _is_changed(self, history):
            StructuralElementHistory.objects.create(
                name=self.name,
                is_actual=self.is_actual,
                unit=self.unit,
                marginal_cost=self.marginal_cost,
                marginal_cost_unique=self.marginal_cost_unique
                str_element_group=self.str_element_group,
                str_element_characteristics=self.str_element_characteristics,
                structural_element=self
            )

    def __str__(self):
        return f'{self.fkr_id} {self.name}'

    def get_absolute_url(self):
        return reverse('fkrdjan:strelement_detail', args=(self.id,))


class StructuralElementHistory(models.Model):
    name = models.CharField(max_length=200)
    is_actual = models.BooleanField()
    unit = models.ForeignKey(Unit, on_delete=models.PROTECT)
    marginal_cost = models.DecimalField(max_digits=10, decimal_places=2)
    marginal_cost_unique = models.DecimalField(max_digits=10, decimal_places=2)
    str_element_characteristics = models.ForeignKey(
        StrElementCharacteristics, on_delete=models.PROTECT)
    str_element_group = models.ForeignKey(
        StructuralElementGroup, on_delete=models.PROTECT)


    structural_element = models.ForeignKey(
        StructuralElement, on_delete=models.PROTECT)
    revision_date = models.DateTimeField()

    def save(self, *args, **kwargs):
        self.revision_date = timezone.now()
        super(StructuralElementHistory, self).save(*args, **kwargs)

    def __str__(self):
        return f'{self.fkr_id} {self.name}'

    def get_absolute_url(self):
        return reverse('fkrdjan:strelement_history_detail', args=(self.id,))


class House(models.Model):
    fias = models.CharField(max_length=36, null=True)
    fk_mhcs = models.IntegerField(null=True)
    fk_asup = models.IntegerField(null=True)
    metropolitan_area = models.ForeignKey(
        MetropolitanArea, on_delete=models.PROTECT)
    address = models.CharField(max_length=512)
    year_of_exploitation = models.IntegerField(null=True)
    year_of_last_work = models.IntegerField(null=True)
    state_of_architecture = models.BooleanField()
    total_square = models.DecimalField(
        null=True, decimal_places=2, max_digits=10)
    count_res_premises = models.IntegerField(null=True)
    count_unres_premises = models.IntegerField(null=True)
    total_square_res = models.DecimalField(
        null=True, decimal_places=2, max_digits=10)
    count_floors = models.IntegerField(null=True)
    count_entrances = models.IntegerField(null=True)
    oktmo = models.CharField(max_length=20, null=True)
    year_of_adding = models.IntegerField(null=True)
    year_of_leaving = models.IntegerField(null=True)
    way_of_form = models.BooleanField()
    date_of_agree_way = models.DateField(null=True)
    bank_bik = models.CharField(max_length=20, null=True)

    def save(self, *args, **kwargs):
        super(House, self).save(*args, **kwargs)
        history = HouseHistory.objects.filter(
            house_id=self.id).order_by('-revision_date').first()
        if _is_changed(self, history):
            HouseHistory.objects.create(
                house=self,
                fias=self.fias,
                fk_mhcs=self.fk_mhcs,
                fk_asup=self.fk_asup,
                metropolitan_area_id=self.metropolitan_area_id,
                address=self.address,
                year_of_exploitation=self.year_of_exploitation,
                year_of_last_work=self.year_of_last_work,
                state_of_architecture=self.state_of_architecture,
                total_square=self.total_square,
                count_res_premises=self.count_res_premises,
                count_unres_premises=self.count_unres_premises,
                total_square_res=self.total_square_res,
                count_floors=self.count_floors,
                count_entrances=self.count_entrances,
                oktmo=self.oktmo,
                year_of_adding=self.year_of_adding,
                year_of_leaving=self.year_of_leaving,
                way_of_form=self.way_of_form,
                date_of_agree_way=self.date_of_agree_way,
                bank_bik=self.bank_bik
            )

    def __str__(self):
        return f'{self.fk_mhcs}-{self.fias}-{self.address}'

    def get_absolute_url(self):
        return reverse('fkrdjan:house_detail', args=(self.id,))


class HouseHistory(models.Model):
    fias = models.CharField(max_length=36, null=True)
    fk_mhcs = models.IntegerField(null=True)
    fk_asup = models.IntegerField(null=True)
    metropolitan_area = models.ForeignKey(
        MetropolitanArea, on_delete=models.PROTECT)
    address = models.CharField(max_length=512)
    year_of_exploitation = models.IntegerField(null=True)
    year_of_last_work = models.IntegerField(null=True)
    state_of_architecture = models.BooleanField()
    total_square = models.DecimalField(
        null=True, decimal_places=2, max_digits=10)
    count_res_premises = models.IntegerField(null=True)
    count_unres_premises = models.IntegerField(null=True)
    total_square_res = models.DecimalField(
        null=True, decimal_places=2, max_digits=10)
    count_floors = models.IntegerField(null=True)
    count_entrances = models.IntegerField(null=True)
    oktmo = models.CharField(max_length=20, null=True)
    year_of_adding = models.IntegerField(null=True)
    year_of_leaving = models.IntegerField(null=True)
    way_of_form = models.BooleanField()
    date_of_agree_way = models.DateField(null=True)
    bank_bik = models.CharField(max_length=20, null=True)

    house = models.ForeignKey(House, on_delete=models.PROTECT)
    revision_date = models.DateTimeField()

    def save(self, *args, **kwargs):
        self.revision_date = timezone.now()
        super(HouseHistory, self).save(*args, **kwargs)

    def __str__(self):
        return f'{self.fk_mhcs} {self.address}'

    def get_absolute_url(self):
        return reverse('fkrdjan:house_history_detail', args=(self.id,))


class Document(models.Model):
    description = models.CharField(max_length=255)
    document = models.FileField(upload_to='documents/')
    uploaded_at = models.DateTimeField(auto_now_add=True)
    status = models.IntegerField(default=0)


class RegionProgram(models.Model):
    number = models.CharField(max_length=255)
    date_created = models.DateTimeField()

    def __str__(self):
        return f'{self.number}/{self.date_created}'


class LinkRegProgramHouse(models.Model):
    year = models.IntegerField()
    region_program = models.ForeignKey(RegionProgram, on_delete=models.PROTECT)
    house = models.ForeignKey(House, on_delete=models.PROTECT)
    structural_element = models.ForeignKey(
        StructuralElement, on_delete=models.PROTECT)

    def save(self, *args, **kwargs):
        super(LinkRegProgramHouse, self).save(*args, **kwargs)
        history = LinkRegProgramHouseHistory.objects.filter(
            link_reg_program_house=self.id).order_by('-revision_date').first()
        if _is_changed(self, history):
            LinkRegProgramHouseHistory.objects.create(
                year=self.year,
                region_program_id=self.region_program_id,
                house_id=self.house_id,
                structural_element_id=self.structural_element_id,
                link_reg_program_house_id=self.id
            )

    def __str__(self):
        return f'{self.region_program}-{self.house}-{self.structural_element}-{self.year}'

    def get_absolute_url(self):
        return reverse('fkrdjan:regprogram_detail', args=(self.id,))


class LinkRegProgramHouseHistory(models.Model):
    year = models.IntegerField()
    region_program = models.ForeignKey(RegionProgram, on_delete=models.PROTECT)
    house = models.ForeignKey(House, on_delete=models.PROTECT)
    structural_element = models.ForeignKey(
        StructuralElement, on_delete=models.PROTECT)

    link_reg_program_house = models.ForeignKey(
        LinkRegProgramHouse, on_delete=models.PROTECT)
    revision_date = models.DateTimeField()

    def save(self, *args, **kwargs):
        self.revision_date = timezone.now()
        super(LinkRegProgramHouseHistory, self).save(*args, **kwargs)

    def __str__(self):
        return f'{self.region_program}-{self.house}-{self.structural_element}-{self.year}'

    def get_absolute_url(self):
        return reverse('fkrdjan:reg_program_history_detail', args=(self.id,))
