import time

from django.core.management.base import BaseCommand
from django.db import transaction

from apps.herd.models import Herd
from apps.livestock.models import LiveStock, LiveStockType
from apps.tag.models import Tag, TemporaryTags
from common.generics import parse_birthdate

BATCH_SIZE = 5000
CHUNK_SIZE = 10000


class Command(BaseCommand):
    help = "Create Livestock if missing and assign Tag if missing"

    def handle(self, *args, **options):

        self.stdout.write(
            self.style.NOTICE(f"Start bulk syncing .... records")
        )

        qs = (
            TemporaryTags.objects
            .filter(sync_status__isnull=True)
            .only('herd_code', 'birthdate', 'gender', 'tag')
        )

        total = qs.count()
        processed = 0
        start_time = time.time()

        LOG_EVERY = 10000

        buffer = []
        for temp in qs.iterator(chunk_size=CHUNK_SIZE):
            buffer.append(temp)

            if len(buffer) >= BATCH_SIZE:
                self.process_batch(buffer)
                processed += len(buffer)
                buffer.clear()

                if processed % LOG_EVERY == 0:
                    elapsed = time.time() - start_time
                    speed = processed / elapsed if elapsed else 0
                    percent = (processed / total) * 100
                    remaining = total - processed
                    eta = remaining / speed if speed else 0

                    self.stdout.write(
                        self.style.NOTICE(
                            f"[SYNC] {processed:,}/{total:,} "
                            f"({percent:.2f}%) | "
                            f"{speed:.0f} rec/s | "
                            f"ETA: {eta / 60:.1f} min"
                        )
                    )

        if buffer:
            self.process_batch(buffer)

        self.stdout.write(self.style.SUCCESS("DONE ✅"))

    def process_batch(self, temps):
        herd_codes = {t.herd_code for t in temps if t.herd_code}

        herds = {
            h.code: h
            for h in Herd.objects.filter(code__in=herd_codes)
        }

        livestocks = LiveStock.objects.filter(
            herd__code__in=herd_codes
        ).select_related('herd').only(
            'id', 'herd_id', 'birthdate', 'gender', 'tag'
        )

        livestock_map = {
            (ls.herd.code, ls.birthdate, ls.gender): ls
            for ls in livestocks
        }

        livestock_types = {
            stock_type.name: stock_type
            for stock_type in LiveStockType.objects.all()
        }

        existing_tags = {
            t.tag_code: t
            for t in Tag.objects.filter(
                tag_code__in=[t.tag for t in temps if t.tag]
            )
        }

        new_livestock = []
        updated_livestock = []
        new_tags = []

        for temp in temps:
            herd = herds.get(temp.herd_code)
            if not herd:
                continue

            birthdate = parse_birthdate(temp.birthdate)
            gender = 1 if temp.gender == 'M' else 2
            livestock_type = livestock_types.get(temp.type)
            weight_type = livestock_type.weight_type

            key = (temp.herd_code, birthdate, gender)
            livestock = livestock_map.get(key)

            if not livestock:
                if not temp.tag:
                    continue

                tag = existing_tags.get(temp.tag)

                if not tag:
                    tag = Tag(tag_code=temp.tag, status='A')
                    new_tags.append(tag)
                    existing_tags[temp.tag] = tag

                livestock = LiveStock(
                    herd=herd,
                    birthdate=birthdate,
                    gender=gender,
                    tag=tag,
                    weight_type=weight_type,
                    type=livestock_type
                )
                new_livestock.append(livestock)
                livestock_map[key] = livestock

                temp.sync_status = 'S'
                continue

            if livestock.tag is None and temp.tag:
                tag = existing_tags.get(temp.tag)

                if not tag:
                    tag = Tag(tag_code=temp.tag, status='A')
                    new_tags.append(tag)
                    existing_tags[temp.tag] = tag

                livestock.tag = tag
                updated_livestock.append(livestock)

                temp.sync_status = 'S'

        with transaction.atomic():
            Tag.objects.bulk_create(new_tags, batch_size=BATCH_SIZE)
            LiveStock.objects.bulk_create(
                new_livestock,
                batch_size=BATCH_SIZE,
                ignore_conflicts=True
            )
            LiveStock.objects.bulk_update(
                updated_livestock,
                ['tag'],
                batch_size=BATCH_SIZE
            )
            TemporaryTags.objects.bulk_update(
                temps,
                ['sync_status'],
                batch_size=BATCH_SIZE
            )
