"""
CLM Repository Implementations.

پیاده‌سازی repositoryهای ماژول مدیریت قراردادها با Django ORM.
"""
from datetime import date, timedelta
from decimal import Decimal
from typing import Optional, List
from uuid import UUID

from django.db.models import Q, Sum

from ..persistence.models import (
    ContractModel,
    ContractPartyModel,
    ContractClauseModel,
    ContractMilestoneModel,
    ContractAmendmentModel,
    ContractTemplateModel,
    ContractRenewalModel,
)


class DjangoContractRepository:
    """پیاده‌سازی مخزن قرارداد با Django ORM."""

    def __init__(self, tenant_id: UUID):
        self._tenant_id = tenant_id

    def _qs(self):
        return ContractModel.objects.filter(tenant_id=self._tenant_id)

    def save(self, data: dict) -> ContractModel:
        instance_id = data.get("id")
        if instance_id:
            try:
                model = ContractModel.objects.get(id=instance_id, tenant_id=self._tenant_id)
                for key, value in data.items():
                    setattr(model, key, value)
                model.save()
                return model
            except ContractModel.DoesNotExist:
                pass
        data["tenant_id"] = self._tenant_id
        return ContractModel.objects.create(**data)

    def get_by_id(self, contract_id: UUID) -> Optional[ContractModel]:
        try:
            return self._qs().get(id=contract_id)
        except ContractModel.DoesNotExist:
            return None

    def get_by_number(self, contract_number: str) -> Optional[ContractModel]:
        try:
            return self._qs().get(contract_number=contract_number)
        except ContractModel.DoesNotExist:
            return None

    def find(self, **criteria) -> List[ContractModel]:
        return list(self._qs().filter(**criteria))

    def find_by_status(self, status: str) -> List[ContractModel]:
        return list(self._qs().filter(status=status))

    def find_by_category(self, category: str) -> List[ContractModel]:
        return list(self._qs().filter(category=category))

    def find_by_project(self, project_id: UUID) -> List[ContractModel]:
        return list(self._qs().filter(project_id=project_id))

    def find_by_party(self, business_partner_id: UUID) -> List[ContractModel]:
        contract_ids = ContractPartyModel.objects.filter(
            tenant_id=self._tenant_id,
            business_partner_id=business_partner_id,
        ).values_list("contract_id", flat=True)
        return list(self._qs().filter(id__in=contract_ids))

    def find_expiring(self, days_ahead: int = 30) -> List[ContractModel]:
        threshold = date.today() + timedelta(days=days_ahead)
        return list(self._qs().filter(
            status="ACTIVE",
            expiry_date__lte=threshold,
            expiry_date__gte=date.today(),
        ))

    def find_expired(self) -> List[ContractModel]:
        return list(self._qs().filter(
            status="ACTIVE",
            expiry_date__lt=date.today(),
        ))

    def delete(self, contract_id: UUID) -> bool:
        deleted, _ = self._qs().filter(id=contract_id, status="DRAFT").delete()
        return deleted > 0

    def count_by_status(self) -> dict:
        from django.db.models import Count
        result = self._qs().values("status").annotate(count=Count("id"))
        return {r["status"]: r["count"] for r in result}

    def total_value_by_status(self, status: str) -> Decimal:
        return self._qs().filter(status=status).aggregate(total=Sum("total_value"))["total"] or Decimal("0")


class DjangoContractPartyRepository:
    """پیاده‌سازی مخزن طرف قرارداد با Django ORM."""

    def __init__(self, tenant_id: UUID):
        self._tenant_id = tenant_id

    def save(self, data: dict) -> ContractPartyModel:
        instance_id = data.get("id")
        if instance_id:
            try:
                model = ContractPartyModel.objects.get(id=instance_id, tenant_id=self._tenant_id)
                for key, value in data.items():
                    setattr(model, key, value)
                model.save()
                return model
            except ContractPartyModel.DoesNotExist:
                pass
        data["tenant_id"] = self._tenant_id
        return ContractPartyModel.objects.create(**data)

    def find_by_contract(self, contract_id: UUID) -> List[ContractPartyModel]:
        return list(ContractPartyModel.objects.filter(
            tenant_id=self._tenant_id, contract_id=contract_id
        ).order_by("order"))

    def delete(self, party_id: UUID) -> bool:
        deleted, _ = ContractPartyModel.objects.filter(
            id=party_id, tenant_id=self._tenant_id
        ).delete()
        return deleted > 0


class DjangoContractClauseRepository:
    """پیاده‌سازی مخزن بند قرارداد با Django ORM."""

    def __init__(self, tenant_id: UUID):
        self._tenant_id = tenant_id

    def save(self, data: dict) -> ContractClauseModel:
        instance_id = data.get("id")
        if instance_id:
            try:
                model = ContractClauseModel.objects.get(id=instance_id, tenant_id=self._tenant_id)
                for key, value in data.items():
                    setattr(model, key, value)
                model.save()
                return model
            except ContractClauseModel.DoesNotExist:
                pass
        data["tenant_id"] = self._tenant_id
        return ContractClauseModel.objects.create(**data)

    def find_by_contract(self, contract_id: UUID) -> List[ContractClauseModel]:
        return list(ContractClauseModel.objects.filter(
            tenant_id=self._tenant_id, contract_id=contract_id
        ).order_by("order"))

    def delete(self, clause_id: UUID) -> bool:
        deleted, _ = ContractClauseModel.objects.filter(
            id=clause_id, tenant_id=self._tenant_id, is_mandatory=False
        ).delete()
        return deleted > 0


class DjangoContractMilestoneRepository:
    """پیاده‌سازی مخزن مرحله قرارداد با Django ORM."""

    def __init__(self, tenant_id: UUID):
        self._tenant_id = tenant_id

    def save(self, data: dict) -> ContractMilestoneModel:
        instance_id = data.get("id")
        if instance_id:
            try:
                model = ContractMilestoneModel.objects.get(id=instance_id, tenant_id=self._tenant_id)
                for key, value in data.items():
                    setattr(model, key, value)
                model.save()
                return model
            except ContractMilestoneModel.DoesNotExist:
                pass
        data["tenant_id"] = self._tenant_id
        return ContractMilestoneModel.objects.create(**data)

    def find_by_contract(self, contract_id: UUID) -> List[ContractMilestoneModel]:
        return list(ContractMilestoneModel.objects.filter(
            tenant_id=self._tenant_id, contract_id=contract_id
        ).order_by("order"))

    def find_overdue(self) -> List[ContractMilestoneModel]:
        return list(ContractMilestoneModel.objects.filter(
            tenant_id=self._tenant_id,
            status__in=["PENDING", "IN_PROGRESS"],
            due_date__lt=date.today(),
        ))

    def delete(self, milestone_id: UUID) -> bool:
        deleted, _ = ContractMilestoneModel.objects.filter(
            id=milestone_id, tenant_id=self._tenant_id
        ).delete()
        return deleted > 0


class DjangoContractAmendmentRepository:
    """پیاده‌سازی مخزن الحاقیه با Django ORM."""

    def __init__(self, tenant_id: UUID):
        self._tenant_id = tenant_id

    def save(self, data: dict) -> ContractAmendmentModel:
        instance_id = data.get("id")
        if instance_id:
            try:
                model = ContractAmendmentModel.objects.get(id=instance_id, tenant_id=self._tenant_id)
                for key, value in data.items():
                    setattr(model, key, value)
                model.save()
                return model
            except ContractAmendmentModel.DoesNotExist:
                pass
        data["tenant_id"] = self._tenant_id
        return ContractAmendmentModel.objects.create(**data)

    def find_by_contract(self, contract_id: UUID) -> List[ContractAmendmentModel]:
        return list(ContractAmendmentModel.objects.filter(
            tenant_id=self._tenant_id, contract_id=contract_id
        ).order_by("amendment_number"))

    def find_pending(self) -> List[ContractAmendmentModel]:
        return list(ContractAmendmentModel.objects.filter(
            tenant_id=self._tenant_id, status="PENDING_APPROVAL"
        ))

    def delete(self, amendment_id: UUID) -> bool:
        deleted, _ = ContractAmendmentModel.objects.filter(
            id=amendment_id, tenant_id=self._tenant_id, status="DRAFT"
        ).delete()
        return deleted > 0


class DjangoContractTemplateRepository:
    """پیاده‌سازی مخزن قالب قرارداد با Django ORM."""

    def __init__(self, tenant_id: UUID):
        self._tenant_id = tenant_id

    def save(self, data: dict) -> ContractTemplateModel:
        instance_id = data.get("id")
        if instance_id:
            try:
                model = ContractTemplateModel.objects.get(id=instance_id, tenant_id=self._tenant_id)
                for key, value in data.items():
                    setattr(model, key, value)
                model.save()
                return model
            except ContractTemplateModel.DoesNotExist:
                pass
        data["tenant_id"] = self._tenant_id
        return ContractTemplateModel.objects.create(**data)

    def get_by_id(self, template_id: UUID) -> Optional[ContractTemplateModel]:
        try:
            return ContractTemplateModel.objects.get(id=template_id, tenant_id=self._tenant_id)
        except ContractTemplateModel.DoesNotExist:
            return None

    def find_active(self, category: str = None) -> List[ContractTemplateModel]:
        qs = ContractTemplateModel.objects.filter(tenant_id=self._tenant_id, is_active=True)
        if category:
            qs = qs.filter(category=category)
        return list(qs.order_by("name"))

    def find_default(self, category: str, contract_type: str = None) -> Optional[ContractTemplateModel]:
        qs = ContractTemplateModel.objects.filter(
            tenant_id=self._tenant_id,
            is_active=True,
            is_default=True,
            category=category,
        )
        if contract_type:
            qs = qs.filter(contract_type=contract_type)
        return qs.first()

    def delete(self, template_id: UUID) -> bool:
        deleted, _ = ContractTemplateModel.objects.filter(
            id=template_id, tenant_id=self._tenant_id
        ).delete()
        return deleted > 0


class DjangoContractRenewalRepository:
    """پیاده‌سازی مخزن تمدید قرارداد با Django ORM."""

    def __init__(self, tenant_id: UUID):
        self._tenant_id = tenant_id

    def save(self, data: dict) -> ContractRenewalModel:
        instance_id = data.get("id")
        if instance_id:
            try:
                model = ContractRenewalModel.objects.get(id=instance_id, tenant_id=self._tenant_id)
                for key, value in data.items():
                    setattr(model, key, value)
                model.save()
                return model
            except ContractRenewalModel.DoesNotExist:
                pass
        data["tenant_id"] = self._tenant_id
        return ContractRenewalModel.objects.create(**data)

    def find_by_contract(self, contract_id: UUID) -> List[ContractRenewalModel]:
        return list(ContractRenewalModel.objects.filter(
            tenant_id=self._tenant_id, contract_id=contract_id
        ).order_by("renewal_number"))

    def delete(self, renewal_id: UUID) -> bool:
        deleted, _ = ContractRenewalModel.objects.filter(
            id=renewal_id, tenant_id=self._tenant_id
        ).delete()
        return deleted > 0
