from django.db import models
from django.utils import timezone
from datetime import datetime
import json
import logging
import requests
from django.db import models, transaction


logger = logging.getLogger(__name__)


class Region(models.Model):
    tripoli_region_id = models.IntegerField(unique=True)
    name = models.CharField(max_length=200, null=True)
    slug = models.CharField(max_length=200, null=True)
    last_updated = models.DateTimeField(auto_now=True)

    def __str__(self):
        return self.name

    @classmethod
    def create_or_update(cls, name, tripoli_region_id, slug):
        region, created = cls.objects.update_or_create(
            tripoli_region_id=tripoli_region_id,
            defaults={'name': name, 'slug': slug}
        )
        return region

    def save(self, *args, **kwargs):
        self.last_updated = timezone.now()
        super(Region, self).save(*args, **kwargs)


class District(models.Model):
    tripoli_district_id = models.IntegerField(unique=True)
    name = models.CharField(max_length=200, null=True)
    region = models.ForeignKey(Region, on_delete=models.CASCADE, null=True)
    slug = models.CharField(max_length=200, null=True)
    last_updated = models.DateTimeField(auto_now=True)

    @classmethod
    def create_or_update(cls, name, tripoli_district_id, slug, region):
        district, created = cls.objects.update_or_create(
            tripoli_district_id=tripoli_district_id,
            defaults={'name': name, 'slug': slug, 'region': region}
        )
        return district

    def save(self, *args, **kwargs):
        self.last_updated = timezone.now()
        super(District, self).save(*args, **kwargs)

    def __str__(self):
        return self.name


class Category(models.Model):
    tripoli_category_id = models.IntegerField()
    slug = models.CharField(max_length=200, null=True)
    name = models.CharField(max_length=200, null=True)
    last_updated = models.DateTimeField(auto_now=True)

    def __str__(self):
        return self.name

    @classmethod
    def create_or_update(cls, name, tripoli_category_id, slug):
        category, created = cls.objects.get_or_create(name=name, slug=slug,
                                                      defaults={'tripoli_category_id': tripoli_category_id})

        if not created:
            cls.objects.filter(name=name, slug=slug, tripoli_category_id=tripoli_category_id).update(
                last_updated=timezone.now())

    def save(self, *args, **kwargs):
        self.last_updated = timezone.now()
        super(Category, self).save(*args, **kwargs)


class Culture(models.Model):
    slug = models.CharField(max_length=200, null=True)
    title = models.CharField(max_length=200, null=True)
    category = models.ForeignKey(Category, on_delete=models.CASCADE, null=True)
    last_updated = models.DateTimeField(auto_now=True)

    @classmethod
    def create_or_update(cls, slug, title, category):
        culture, created = cls.objects.get_or_create(slug=slug, category=category, title=title)

        if not created:
            cls.objects.filter(slug=slug, category=category, title=title).update(last_updated=timezone.now())

    def save(self, *args, **kwargs):
        self.last_updated = timezone.now()
        super(Culture, self).save(*args, **kwargs)


class CultureableName(models.Model):
    cultureable_name = models.CharField(max_length=200, unique=True)
    last_updated = models.DateTimeField(auto_now=True)
    active = models.BooleanField(default=True)

    def save(self, *args, **kwargs):
        self.last_updated = timezone.now()
        super(CultureableName, self).save(*args, **kwargs)

    def __str__(self):
        return self.cultureable_name


class CatalogLandingFields(models.Model):
    cultureable_name = models.ForeignKey(CultureableName, on_delete=models.CASCADE)
    cultureable_units = models.CharField(max_length=100, null=True, blank=True)
    culture_area = models.FloatField(null=True, blank=True)
    collected = models.FloatField(null=True, blank=True)
    year = models.IntegerField(null=True, blank=True)
    last_updated = models.DateTimeField(auto_now=True)

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

    def save(self, *args, **kwargs):
        self.last_updated = timezone.now()
        super(CatalogLandingFields, self).save(*args, **kwargs)


class CatalogMachinery(models.Model):
    code = models.IntegerField(unique=True)
    name = models.CharField(max_length=200)
    last_updated = models.DateTimeField(auto_now=True)

    def save(self, *args, **kwargs):
        self.last_updated = timezone.now()
        super(CatalogMachinery, self).save(*args, **kwargs)


class Machinery(models.Model):
    catalog_machinery = models.ForeignKey('farmerapp.CatalogMachinery', on_delete=models.CASCADE)
    availability = models.IntegerField()
    received = models.IntegerField()
    eliminated = models.IntegerField()
    farmer = models.ForeignKey('farmerapp.Farmer', on_delete=models.CASCADE)
    last_updated = models.DateTimeField(auto_now=True)

    def save(self, *args, **kwargs):
        self.last_updated = timezone.now()
        super(Machinery, self).save(*args, **kwargs)


class Farmer(models.Model):
    CLIENT_STATUS_CHOICES = [
        ('unprocessed', 'Не оброблений'),
        ('cold', 'Холодний'),
        ('warm', 'Теплий'),
        ('hot', 'Гарячий'),
    ]

    tripoli_farmer_id = models.IntegerField(unique=True, null=True, blank=True)
    erdpou = models.IntegerField(null=True, blank=True)
    kod_pdv = models.BigIntegerField(null=True, blank=True)
    registration_at = models.DateField(null=True, blank=True)
    district = models.ForeignKey(District, on_delete=models.SET_NULL, null=True, related_name='farmers')
    region = models.ForeignKey(Region, on_delete=models.SET_NULL, null=True, blank=True)
    address = models.CharField(max_length=10000, null=True)
    email = models.CharField(max_length=10000, null=True, blank=True)
    website = models.CharField(max_length=10000, null=True, blank=True)
    director = models.CharField(max_length=10000, null=True)
    description = models.TextField(null=True, blank=True)
    name = models.CharField(max_length=10000, null=True)
    short_name = models.CharField(max_length=10000, null=True)
    atu_code = models.CharField(max_length=10000, null=True)
    record_number = models.CharField(max_length=10000, null=True)
    record_date = models.DateField(null=True, blank=True)
    phone_1 = models.CharField(max_length=50, null=True)
    phone_2 = models.CharField(max_length=50, null=True)
    phone_3 = models.CharField(max_length=50, null=True)
    phone_4 = models.CharField(max_length=50, null=True)
    phone_5 = models.CharField(max_length=50, null=True)
    phone_6 = models.CharField(max_length=50, null=True)
    phone_7 = models.CharField(max_length=50, null=True)
    phone_mobile_1 = models.CharField(max_length=50, null=True)
    phone_mobile_2 = models.CharField(max_length=50, null=True)
    phone_mobile_3 = models.CharField(max_length=50, null=True)
    phone_mobile_4 = models.CharField(max_length=50, null=True)
    phone_mobile_5 = models.CharField(max_length=50, null=True)
    org_s_status = models.CharField(max_length=200, null=True)
    landing_area = models.FloatField(null=True)
    statut_kapital = models.BigIntegerField(null=True)
    phone_mobile_1_viber = models.CharField(max_length=200, null=True)
    phone_mobile_1_telegram = models.CharField(max_length=200, null=True)
    phone_mobile_2_viber = models.CharField(max_length=200, null=True)
    phone_mobile_2_telegram = models.CharField(max_length=200, null=True)
    phone_mobile_3_viber = models.CharField(max_length=200, null=True)
    phone_mobile_3_telegram = models.CharField(max_length=200, null=True)
    last_updated = models.DateTimeField(auto_now=True)
    contact_persons = models.ManyToManyField('contacts.ContactPerson', related_name='farmers', blank=True)
    client_status = models.CharField(max_length=20, choices=CLIENT_STATUS_CHOICES, default='unprocessed')

    categories = models.ManyToManyField('Category', blank=True)
    catalog_machineries = models.ManyToManyField('CatalogMachinery', related_name='farmers', blank=True)
    catalog_landing_fields = models.ManyToManyField('CatalogLandingFields', blank=True)

    cultureable_names = models.ManyToManyField('CultureableName', related_name='farmers', blank=True)

    def __str__(self):
        return self.name or "Unnamed Farmer"

    @classmethod
    def update_or_create_from_json(cls, data):
        allowed_fields = [
            'tripoli_farmer_id', 'erdpou', 'kod_pdv', 'registration_at', 'address',
            'email', 'website', 'director', 'description', 'name', 'short_name',
            'atu_code', 'record_number', 'record_date', 'phone_1', 'phone_2', 'phone_3', 'phone_4', 'phone_5', 'phone_6', 'phone_7',
            'org_s_status', 'landing_area', 'statut_kapital', 'phone_mobile_1', 'phone_mobile_2', 'phone_mobile_3',
            'phone_mobile_4', 'phone_mobile_5',
            'phone_mobile_1_viber', 'phone_mobile_1_telegram', 'phone_mobile_2_viber',
            'phone_mobile_2_telegram', 'phone_mobile_3_viber', 'phone_mobile_3_telegram',
            'district', 'region'
        ]

        required_fields = ['district_id', 'region_id']
        for field in required_fields:
            if field not in data or data[field] is None:
                return None, {"error": f"Error processing farmer data: '{field}' is required or can't be null."}

        filtered_data = {k: v for k, v in data.items() if k in allowed_fields}

        # Обробка дат
        registration_at = filtered_data.get('registration_at')
        if registration_at:
            try:
                filtered_data['registration_at'] = datetime.strptime(registration_at, '%d.%m.%Y').date()
            except ValueError as e:
                logger.error(f"Error parsing registration_at: {e}")
                filtered_data['registration_at'] = None

        record_date = filtered_data.get('record_date')
        if record_date:
            try:
                filtered_data['record_date'] = datetime.strptime(record_date, '%Y-%m-%d').date()
            except ValueError as e:
                logger.error(f"Error parsing record_date: {e}")
                filtered_data['record_date'] = None

        district_name = data.get('district')
        district_id = data.get('district_id')
        district_slug = data.get('district_slug')
        region_name = data.get('region')
        region_id = data.get('region_id')
        region_slug = data.get('region_slug')

        # Обробка регіону
        try:
            region, _ = Region.objects.get_or_create(
                tripoli_region_id=region_id,
                defaults={'name': region_name, 'slug': region_slug}
            )
        except Exception as e:
            logger.error(f"Error processing region data: {e}")
            return None, {"error": f"Error processing region data: {str(e)}"}

        # Обробка району
        try:
            district, _ = District.objects.get_or_create(
                tripoli_district_id=district_id,
                defaults={'name': district_name, 'slug': district_slug, 'region': region}
            )
        except Exception as e:
            logger.error(f"Error processing district data: {e}")
            return None, {"error": f"Error processing district data: {str(e)}"}

        # Призначення району та регіону фермеру
        filtered_data['district'] = district
        filtered_data['region'] = region

        filtered_data.pop('district_id', None)
        filtered_data.pop('region_id', None)

        farmer_id = filtered_data.pop('tripoli_farmer_id', None)

        existing_farmer = cls.objects.filter(tripoli_farmer_id=farmer_id).first()

        if existing_farmer:
            obj = existing_farmer
            created = False

            for key, value in filtered_data.items():
                setattr(obj, key, value)

            obj.district = district
            obj.region = region

            try:
                obj.save()
                logger.info(f"Updated farmer: {obj}")
            except Exception as e:
                logger.error(f"Error saving farmer data: {e}")
                return None, {"error": f"Error saving farmer data: {str(e)}"}
        else:
            try:
                obj = cls.objects.create(tripoli_farmer_id=farmer_id, **filtered_data)
                created = True
                logger.info(f"Created new farmer: {obj}")
            except Exception as e:
                logger.error(f"Error creating farmer data: {e}")
                return None, {"error": f"Error creating farmer data: {str(e)}"}

        # Оновлення зв'язаних даних

        # Категорії
        if 'categories' in data:
            obj.categories.clear()
            for category_data in data['categories']:
                try:
                    if 'tripoli_category_id' in category_data:
                        category = Category.objects.get(tripoli_category_id=category_data['tripoli_category_id'])
                        obj.categories.add(category)
                except Category.DoesNotExist:
                    pass

        # Техніка
        if 'catalog_machineries' in data:
            obj.catalog_machineries.clear()
            for machinery_data in data['catalog_machineries']:
                try:
                    machinery = CatalogMachinery.objects.get(code=machinery_data['code'])
                    obj.catalog_machineries.add(machinery)
                except CatalogMachinery.DoesNotExist:
                    pass

        # Посівні площі
        if 'catalog_landing_fields' in data:
            obj.catalog_landing_fields.clear()
            for landing_data in data['catalog_landing_fields']:
                try:
                    cultureable_name, _ = CultureableName.objects.get_or_create(
                        cultureable_name=landing_data['cultureable_name'])
                    collected_value = None
                    if landing_data.get('collected'):
                        try:
                            collected_value = float(str(landing_data['collected']).replace(',', '.'))
                        except ValueError:
                            logger.error("Error processing 'collected' value")
                            return None, {"error": "Error processing 'collected' value: could not convert to float."}

                    landing_field, _ = CatalogLandingFields.objects.get_or_create(
                        cultureable_name=cultureable_name,
                        cultureable_units=landing_data.get('cultureable_units'),
                        culture_area=landing_data.get('culture_area'),
                        collected=collected_value,
                        year=landing_data.get('year')
                    )
                    obj.catalog_landing_fields.add(landing_field)
                except CultureableName.DoesNotExist:
                    pass

        return obj, {"created": created, "data": filtered_data}

    def save(self, *args, **kwargs):
        self.last_updated = timezone.now()
        super(Farmer, self).save(*args, **kwargs)


class CulturesAvailability(models.Model):
    culture = models.ForeignKey(Culture, on_delete=models.CASCADE)
    availability = models.FloatField()
    collected = models.FloatField()
    year = models.IntegerField()
    last_updated = models.DateTimeField(auto_now=True)

    @classmethod
    def update_or_create_from_json(cls, data):
        try:
            culture = Culture.objects.get(slug=data['culture']['slug'])
        except Culture.DoesNotExist:
            return None, {"error": "Culture not found."}

        obj, created = cls.objects.update_or_create(
            culture=culture,
            year=data['year'],
            defaults={
                'availability': data['availability'],
                'collected': data['collected']
            }
        )
        return obj, {"created": created}

    def save(self, *args, **kwargs):
        self.last_updated = timezone.now()
        super(CulturesAvailability, self).save(*args, **kwargs)
