"""
PM Repository Implementations — Supporting Entities.

پیاده‌سازی repositoryهای ریسک، بیس‌لاین، تقویم، درخواست تغییر،
تایم‌شیت، عضو پروژه، نظر و لاگ فعالیت.
"""
from datetime import date
from typing import Optional, List
from uuid import UUID

from django.db.models import Q, Sum, Count

from ...domain.entities.risk import Risk
from ...domain.entities.supporting import (
    Baseline, Calendar, Holiday, ChangeRequest,
    Timesheet, ProjectMember, Comment, ActivityLog,
)
from ...domain.entities.issue import Issue
from ...domain.entities.obs import OBSNode
from ...domain.entities.rbs import RBSNode
from ...domain.entities.quality import (
    QualityPlan, Inspection, NCR, QualityChecklist, QualityChecklistItem,
)
from ...domain.entities.project_document import ProjectDocument
from ...domain.repositories.interfaces import (
    IRiskRepository,
    IBaselineRepository,
    ICalendarRepository,
    IHolidayRepository,
    IChangeRequestRepository,
    ITimesheetRepository,
    IProjectMemberRepository,
    ICommentRepository,
    IActivityLogRepository,
    IIssueRepository,
    IOBSNodeRepository,
    IRBSNodeRepository,
    IQualityPlanRepository,
    IInspectionRepository,
    INCRRepository,
    IQualityChecklistRepository,
    IQualityChecklistItemRepository,
    IProjectDocumentRepository,
)
from ..persistence.models import (
    RiskModel,
    BaselineModel,
    CalendarModel,
    HolidayModel,
    ChangeRequestModel,
    TimesheetModel,
    ProjectMemberModel,
    CommentModel,
    ActivityLogModel,
    IssueModel,
    OBSNodeModel,
    RBSNodeModel,
    QualityPlanModel,
    InspectionModel,
    NCRModel,
    QualityChecklistModel,
    QualityChecklistItemModel,
    ProjectDocumentModel,
)
from .mappers import (
    RiskMapper,
    BaselineMapper,
    CalendarMapper,
    HolidayMapper,
    ChangeRequestMapper,
    TimesheetMapper,
    ProjectMemberMapper,
    CommentMapper,
    ActivityLogMapper,
    IssueMapper,
    OBSNodeMapper,
    RBSNodeMapper,
    QualityPlanMapper,
    InspectionMapper,
    NCRMapper,
    QualityChecklistMapper,
    QualityChecklistItemMapper,
    ProjectDocumentMapper,
)


# ═══════════════════════════════════════════════════
# Risk Repository
# ═══════════════════════════════════════════════════

class DjangoRiskRepository(IRiskRepository):
    """پیاده‌سازی مخزن ریسک با Django ORM."""

    def __init__(self, tenant_id: UUID):
        self._tenant_id = tenant_id

    def save(self, entity: Risk) -> Risk:
        try:
            model = RiskModel.objects.get(id=entity.id)
            model = RiskMapper.to_model(entity, model)
        except RiskModel.DoesNotExist:
            model = RiskMapper.to_model(entity)
        model.save()
        return RiskMapper.to_entity(model)

    def get_by_id(self, id: UUID) -> Optional[Risk]:
        try:
            model = RiskModel.objects.get(id=id, tenant_id=self._tenant_id)
            return RiskMapper.to_entity(model)
        except RiskModel.DoesNotExist:
            return None

    def find(self, **criteria) -> List[Risk]:
        qs = RiskModel.objects.filter(tenant_id=self._tenant_id, **criteria)
        return [RiskMapper.to_entity(m) for m in qs]

    def delete(self, id: UUID) -> bool:
        deleted, _ = RiskModel.objects.filter(
            id=id, tenant_id=self._tenant_id
        ).delete()
        return deleted > 0

    def find_by_project(self, project_id: UUID) -> List[Risk]:
        qs = RiskModel.objects.filter(
            tenant_id=self._tenant_id, project_id=project_id
        )
        return [RiskMapper.to_entity(m) for m in qs]

    def find_open_risks(self, project_id: UUID) -> List[Risk]:
        qs = RiskModel.objects.filter(
            tenant_id=self._tenant_id,
            project_id=project_id,
        ).exclude(status__in=["closed", "occurred"])
        return [RiskMapper.to_entity(m) for m in qs]

    def find_high_risks(self, project_id: UUID) -> List[Risk]:
        qs = RiskModel.objects.filter(
            tenant_id=self._tenant_id,
            project_id=project_id,
            probability__in=["high", "very_high"],
            impact__in=["major", "critical"],
        ).exclude(status="closed")
        return [RiskMapper.to_entity(m) for m in qs]

    def count_by_status(self, project_id: UUID) -> dict:
        qs = RiskModel.objects.filter(
            tenant_id=self._tenant_id, project_id=project_id
        ).values("status").annotate(count=Count("id"))
        return {item["status"]: item["count"] for item in qs}


# ═══════════════════════════════════════════════════
# Baseline Repository
# ═══════════════════════════════════════════════════

class DjangoBaselineRepository(IBaselineRepository):
    """پیاده‌سازی مخزن خط پایه با Django ORM."""

    def __init__(self, tenant_id: UUID):
        self._tenant_id = tenant_id

    def save(self, entity: Baseline) -> Baseline:
        try:
            model = BaselineModel.objects.get(id=entity.id)
            model = BaselineMapper.to_model(entity, model)
        except BaselineModel.DoesNotExist:
            model = BaselineMapper.to_model(entity)
        model.save()
        return BaselineMapper.to_entity(model)

    def get_by_id(self, id: UUID) -> Optional[Baseline]:
        try:
            model = BaselineModel.objects.get(id=id, tenant_id=self._tenant_id)
            return BaselineMapper.to_entity(model)
        except BaselineModel.DoesNotExist:
            return None

    def find(self, **criteria) -> List[Baseline]:
        qs = BaselineModel.objects.filter(tenant_id=self._tenant_id, **criteria)
        return [BaselineMapper.to_entity(m) for m in qs]

    def delete(self, id: UUID) -> bool:
        deleted, _ = BaselineModel.objects.filter(
            id=id, tenant_id=self._tenant_id
        ).delete()
        return deleted > 0

    def find_by_project(self, project_id: UUID) -> List[Baseline]:
        qs = BaselineModel.objects.filter(
            tenant_id=self._tenant_id, project_id=project_id
        ).order_by("-created_at")
        return [BaselineMapper.to_entity(m) for m in qs]

    def find_active(self, project_id: UUID) -> Optional[Baseline]:
        try:
            model = BaselineModel.objects.get(
                tenant_id=self._tenant_id,
                project_id=project_id,
                is_active=True,
            )
            return BaselineMapper.to_entity(model)
        except BaselineModel.DoesNotExist:
            return None

    def deactivate_all(self, project_id: UUID) -> int:
        return BaselineModel.objects.filter(
            tenant_id=self._tenant_id,
            project_id=project_id,
            is_active=True,
        ).update(is_active=False)


# ═══════════════════════════════════════════════════
# Calendar & Holiday Repositories
# ═══════════════════════════════════════════════════

class DjangoCalendarRepository(ICalendarRepository):
    """پیاده‌سازی مخزن تقویم با Django ORM."""

    def __init__(self, tenant_id: UUID):
        self._tenant_id = tenant_id

    def save(self, entity: Calendar) -> Calendar:
        try:
            model = CalendarModel.objects.get(id=entity.id)
            model = CalendarMapper.to_model(entity, model)
        except CalendarModel.DoesNotExist:
            model = CalendarMapper.to_model(entity)
        model.save()
        return CalendarMapper.to_entity(model)

    def get_by_id(self, id: UUID) -> Optional[Calendar]:
        try:
            model = CalendarModel.objects.get(id=id, tenant_id=self._tenant_id)
            return CalendarMapper.to_entity(model)
        except CalendarModel.DoesNotExist:
            return None

    def find(self, **criteria) -> List[Calendar]:
        qs = CalendarModel.objects.filter(tenant_id=self._tenant_id, **criteria)
        return [CalendarMapper.to_entity(m) for m in qs]

    def delete(self, id: UUID) -> bool:
        deleted, _ = CalendarModel.objects.filter(
            id=id, tenant_id=self._tenant_id
        ).delete()
        return deleted > 0

    def find_default(self) -> Optional[Calendar]:
        try:
            model = CalendarModel.objects.get(
                tenant_id=self._tenant_id, is_default=True
            )
            return CalendarMapper.to_entity(model)
        except CalendarModel.DoesNotExist:
            return None

    def find_all(self) -> List[Calendar]:
        qs = CalendarModel.objects.filter(tenant_id=self._tenant_id)
        return [CalendarMapper.to_entity(m) for m in qs]


class DjangoHolidayRepository(IHolidayRepository):
    """پیاده‌سازی مخزن تعطیلات با Django ORM."""

    def __init__(self, tenant_id: UUID):
        self._tenant_id = tenant_id

    def save(self, entity: Holiday) -> Holiday:
        try:
            model = HolidayModel.objects.get(id=entity.id)
            model = HolidayMapper.to_model(entity, model)
        except HolidayModel.DoesNotExist:
            model = HolidayMapper.to_model(entity)
        model.save()
        return HolidayMapper.to_entity(model)

    def get_by_id(self, id: UUID) -> Optional[Holiday]:
        try:
            model = HolidayModel.objects.get(id=id, tenant_id=self._tenant_id)
            return HolidayMapper.to_entity(model)
        except HolidayModel.DoesNotExist:
            return None

    def find(self, **criteria) -> List[Holiday]:
        qs = HolidayModel.objects.filter(tenant_id=self._tenant_id, **criteria)
        return [HolidayMapper.to_entity(m) for m in qs]

    def delete(self, id: UUID) -> bool:
        deleted, _ = HolidayModel.objects.filter(
            id=id, tenant_id=self._tenant_id
        ).delete()
        return deleted > 0

    def find_by_calendar(self, calendar_id: UUID) -> List[Holiday]:
        qs = HolidayModel.objects.filter(
            tenant_id=self._tenant_id, calendar_id=calendar_id
        ).order_by("holiday_date")
        return [HolidayMapper.to_entity(m) for m in qs]

    def find_in_range(self, calendar_id: UUID, start: date, end: date) -> List[Holiday]:
        qs = HolidayModel.objects.filter(
            tenant_id=self._tenant_id,
            calendar_id=calendar_id,
            holiday_date__gte=start,
            holiday_date__lte=end,
        ).order_by("holiday_date")
        return [HolidayMapper.to_entity(m) for m in qs]


# ═══════════════════════════════════════════════════
# Change Request Repository
# ═══════════════════════════════════════════════════

class DjangoChangeRequestRepository(IChangeRequestRepository):
    """پیاده‌سازی مخزن درخواست تغییر با Django ORM."""

    def __init__(self, tenant_id: UUID):
        self._tenant_id = tenant_id

    def save(self, entity: ChangeRequest) -> ChangeRequest:
        try:
            model = ChangeRequestModel.objects.get(id=entity.id)
            model = ChangeRequestMapper.to_model(entity, model)
        except ChangeRequestModel.DoesNotExist:
            model = ChangeRequestMapper.to_model(entity)
        model.save()
        return ChangeRequestMapper.to_entity(model)

    def get_by_id(self, id: UUID) -> Optional[ChangeRequest]:
        try:
            model = ChangeRequestModel.objects.get(id=id, tenant_id=self._tenant_id)
            return ChangeRequestMapper.to_entity(model)
        except ChangeRequestModel.DoesNotExist:
            return None

    def find(self, **criteria) -> List[ChangeRequest]:
        qs = ChangeRequestModel.objects.filter(tenant_id=self._tenant_id, **criteria)
        return [ChangeRequestMapper.to_entity(m) for m in qs]

    def delete(self, id: UUID) -> bool:
        deleted, _ = ChangeRequestModel.objects.filter(
            id=id, tenant_id=self._tenant_id
        ).delete()
        return deleted > 0

    def find_by_project(self, project_id: UUID) -> List[ChangeRequest]:
        qs = ChangeRequestModel.objects.filter(
            tenant_id=self._tenant_id, project_id=project_id
        ).order_by("-created_at")
        return [ChangeRequestMapper.to_entity(m) for m in qs]

    def find_pending(self, project_id: UUID) -> List[ChangeRequest]:
        qs = ChangeRequestModel.objects.filter(
            tenant_id=self._tenant_id,
            project_id=project_id,
            status__in=["submitted", "under_review"],
        ).order_by("-created_at")
        return [ChangeRequestMapper.to_entity(m) for m in qs]

    def find_by_status(self, project_id: UUID, status: str) -> List[ChangeRequest]:
        qs = ChangeRequestModel.objects.filter(
            tenant_id=self._tenant_id,
            project_id=project_id,
            status=status,
        )
        return [ChangeRequestMapper.to_entity(m) for m in qs]


# ═══════════════════════════════════════════════════
# Timesheet Repository
# ═══════════════════════════════════════════════════

class DjangoTimesheetRepository(ITimesheetRepository):
    """پیاده‌سازی مخزن تایم‌شیت با Django ORM."""

    def __init__(self, tenant_id: UUID):
        self._tenant_id = tenant_id

    def save(self, entity: Timesheet) -> Timesheet:
        try:
            model = TimesheetModel.objects.get(id=entity.id)
            model = TimesheetMapper.to_model(entity, model)
        except TimesheetModel.DoesNotExist:
            model = TimesheetMapper.to_model(entity)
        model.save()
        return TimesheetMapper.to_entity(model)

    def get_by_id(self, id: UUID) -> Optional[Timesheet]:
        try:
            model = TimesheetModel.objects.get(id=id, tenant_id=self._tenant_id)
            return TimesheetMapper.to_entity(model)
        except TimesheetModel.DoesNotExist:
            return None

    def find(self, **criteria) -> List[Timesheet]:
        qs = TimesheetModel.objects.filter(tenant_id=self._tenant_id, **criteria)
        return [TimesheetMapper.to_entity(m) for m in qs]

    def delete(self, id: UUID) -> bool:
        deleted, _ = TimesheetModel.objects.filter(
            id=id, tenant_id=self._tenant_id
        ).delete()
        return deleted > 0

    def find_by_project(self, project_id: UUID) -> List[Timesheet]:
        qs = TimesheetModel.objects.filter(
            tenant_id=self._tenant_id, project_id=project_id
        ).order_by("-work_date")
        return [TimesheetMapper.to_entity(m) for m in qs]

    def find_by_user(self, user_id: UUID, start: date, end: date) -> List[Timesheet]:
        qs = TimesheetModel.objects.filter(
            tenant_id=self._tenant_id,
            user_id=user_id,
            work_date__gte=start,
            work_date__lte=end,
        ).order_by("work_date")
        return [TimesheetMapper.to_entity(m) for m in qs]

    def find_pending_approval(self, project_id: UUID) -> List[Timesheet]:
        qs = TimesheetModel.objects.filter(
            tenant_id=self._tenant_id,
            project_id=project_id,
            status="submitted",
        ).order_by("-work_date")
        return [TimesheetMapper.to_entity(m) for m in qs]

    def get_total_hours(self, project_id: UUID, user_id: Optional[UUID] = None) -> dict:
        qs = TimesheetModel.objects.filter(
            tenant_id=self._tenant_id,
            project_id=project_id,
            status="approved",
        )
        if user_id:
            qs = qs.filter(user_id=user_id)
        result = qs.aggregate(
            total_regular=Sum("regular_hours"),
            total_overtime=Sum("overtime_hours"),
        )
        return {
            "total_regular": result["total_regular"] or 0,
            "total_overtime": result["total_overtime"] or 0,
        }


# ═══════════════════════════════════════════════════
# Project Member Repository
# ═══════════════════════════════════════════════════

class DjangoProjectMemberRepository(IProjectMemberRepository):
    """پیاده‌سازی مخزن عضو پروژه با Django ORM."""

    def __init__(self, tenant_id: UUID):
        self._tenant_id = tenant_id

    def save(self, entity: ProjectMember) -> ProjectMember:
        try:
            model = ProjectMemberModel.objects.get(id=entity.id)
            model = ProjectMemberMapper.to_model(entity, model)
        except ProjectMemberModel.DoesNotExist:
            model = ProjectMemberMapper.to_model(entity)
        model.save()
        return ProjectMemberMapper.to_entity(model)

    def get_by_id(self, id: UUID) -> Optional[ProjectMember]:
        try:
            model = ProjectMemberModel.objects.get(id=id, tenant_id=self._tenant_id)
            return ProjectMemberMapper.to_entity(model)
        except ProjectMemberModel.DoesNotExist:
            return None

    def find(self, **criteria) -> List[ProjectMember]:
        qs = ProjectMemberModel.objects.filter(tenant_id=self._tenant_id, **criteria)
        return [ProjectMemberMapper.to_entity(m) for m in qs]

    def delete(self, id: UUID) -> bool:
        deleted, _ = ProjectMemberModel.objects.filter(
            id=id, tenant_id=self._tenant_id
        ).delete()
        return deleted > 0

    def find_by_project(self, project_id: UUID) -> List[ProjectMember]:
        qs = ProjectMemberModel.objects.filter(
            tenant_id=self._tenant_id, project_id=project_id, is_active=True
        )
        return [ProjectMemberMapper.to_entity(m) for m in qs]

    def find_by_user(self, user_id: UUID) -> List[ProjectMember]:
        qs = ProjectMemberModel.objects.filter(
            tenant_id=self._tenant_id, user_id=user_id, is_active=True
        )
        return [ProjectMemberMapper.to_entity(m) for m in qs]

    def is_member(self, project_id: UUID, user_id: UUID) -> bool:
        return ProjectMemberModel.objects.filter(
            tenant_id=self._tenant_id,
            project_id=project_id,
            user_id=user_id,
            is_active=True,
        ).exists()

    def get_role(self, project_id: UUID, user_id: UUID) -> Optional[str]:
        try:
            model = ProjectMemberModel.objects.get(
                tenant_id=self._tenant_id,
                project_id=project_id,
                user_id=user_id,
                is_active=True,
            )
            return model.role
        except ProjectMemberModel.DoesNotExist:
            return None


# ═══════════════════════════════════════════════════
# Comment Repository
# ═══════════════════════════════════════════════════

class DjangoCommentRepository(ICommentRepository):
    """پیاده‌سازی مخزن نظر با Django ORM."""

    def __init__(self, tenant_id: UUID):
        self._tenant_id = tenant_id

    def save(self, entity: Comment) -> Comment:
        try:
            model = CommentModel.objects.get(id=entity.id)
            model = CommentMapper.to_model(entity, model)
        except CommentModel.DoesNotExist:
            model = CommentMapper.to_model(entity)
        model.save()
        return CommentMapper.to_entity(model)

    def get_by_id(self, id: UUID) -> Optional[Comment]:
        try:
            model = CommentModel.objects.get(id=id, tenant_id=self._tenant_id)
            return CommentMapper.to_entity(model)
        except CommentModel.DoesNotExist:
            return None

    def find(self, **criteria) -> List[Comment]:
        qs = CommentModel.objects.filter(tenant_id=self._tenant_id, **criteria)
        return [CommentMapper.to_entity(m) for m in qs]

    def delete(self, id: UUID) -> bool:
        deleted, _ = CommentModel.objects.filter(
            id=id, tenant_id=self._tenant_id
        ).delete()
        return deleted > 0

    def find_by_project(self, project_id: UUID) -> List[Comment]:
        qs = CommentModel.objects.filter(
            tenant_id=self._tenant_id, project_id=project_id, is_deleted=False
        ).order_by("created_at")
        return [CommentMapper.to_entity(m) for m in qs]

    def find_by_task(self, task_id: UUID) -> List[Comment]:
        qs = CommentModel.objects.filter(
            tenant_id=self._tenant_id, task_id=task_id, is_deleted=False
        ).order_by("created_at")
        return [CommentMapper.to_entity(m) for m in qs]

    def find_root_comments(self, project_id: UUID, task_id: Optional[UUID] = None) -> List[Comment]:
        qs = CommentModel.objects.filter(
            tenant_id=self._tenant_id,
            project_id=project_id,
            parent__isnull=True,
            is_deleted=False,
        )
        if task_id:
            qs = qs.filter(task_id=task_id)
        return [CommentMapper.to_entity(m) for m in qs.order_by("created_at")]

    def find_replies(self, comment_id: UUID) -> List[Comment]:
        qs = CommentModel.objects.filter(
            tenant_id=self._tenant_id,
            parent_id=comment_id,
            is_deleted=False,
        ).order_by("created_at")
        return [CommentMapper.to_entity(m) for m in qs]


# ═══════════════════════════════════════════════════
# Activity Log Repository
# ═══════════════════════════════════════════════════

class DjangoActivityLogRepository(IActivityLogRepository):
    """پیاده‌سازی مخزن لاگ فعالیت با Django ORM."""

    def __init__(self, tenant_id: UUID):
        self._tenant_id = tenant_id

    def save(self, entity: ActivityLog) -> ActivityLog:
        model = ActivityLogMapper.to_model(entity)
        model.save()
        return ActivityLogMapper.to_entity(model)

    def get_by_id(self, id: UUID) -> Optional[ActivityLog]:
        try:
            model = ActivityLogModel.objects.get(id=id, tenant_id=self._tenant_id)
            return ActivityLogMapper.to_entity(model)
        except ActivityLogModel.DoesNotExist:
            return None

    def find(self, **criteria) -> List[ActivityLog]:
        qs = ActivityLogModel.objects.filter(tenant_id=self._tenant_id, **criteria)
        return [ActivityLogMapper.to_entity(m) for m in qs]

    def delete(self, id: UUID) -> bool:
        # لاگ‌ها حذف نمی‌شوند
        return False

    def find_by_project(self, project_id: UUID, limit: int = 50) -> List[ActivityLog]:
        qs = ActivityLogModel.objects.filter(
            tenant_id=self._tenant_id, project_id=project_id
        ).order_by("-created_at")[:limit]
        return [ActivityLogMapper.to_entity(m) for m in qs]

    def find_by_entity(self, entity_type: str, entity_id: UUID) -> List[ActivityLog]:
        qs = ActivityLogModel.objects.filter(
            tenant_id=self._tenant_id,
            entity_type=entity_type,
            entity_id=entity_id,
        ).order_by("-created_at")
        return [ActivityLogMapper.to_entity(m) for m in qs]

    def find_by_user(self, user_id: UUID, limit: int = 50) -> List[ActivityLog]:
        qs = ActivityLogModel.objects.filter(
            tenant_id=self._tenant_id, user_id=user_id
        ).order_by("-created_at")[:limit]
        return [ActivityLogMapper.to_entity(m) for m in qs]


# ═══════════════════════════════════════════════════
# Issue Repository
# ═══════════════════════════════════════════════════

class DjangoIssueRepository(IIssueRepository):
    """پیاده‌سازی مخزن مسئله با Django ORM."""

    def __init__(self, tenant_id: UUID):
        self._tenant_id = tenant_id

    def save(self, entity: Issue) -> Issue:
        try:
            model = IssueModel.objects.get(id=entity.id)
            model = IssueMapper.to_model(entity, model)
        except IssueModel.DoesNotExist:
            model = IssueMapper.to_model(entity)
        model.save()
        return IssueMapper.to_entity(model)

    def get_by_id(self, id: UUID) -> Optional[Issue]:
        try:
            model = IssueModel.objects.get(id=id, tenant_id=self._tenant_id)
            return IssueMapper.to_entity(model)
        except IssueModel.DoesNotExist:
            return None

    def find(self, **criteria) -> List[Issue]:
        qs = IssueModel.objects.filter(tenant_id=self._tenant_id, **criteria)
        return [IssueMapper.to_entity(m) for m in qs]

    def delete(self, id: UUID) -> bool:
        deleted, _ = IssueModel.objects.filter(
            id=id, tenant_id=self._tenant_id
        ).delete()
        return deleted > 0

    def find_by_project(self, project_id: UUID) -> List[Issue]:
        qs = IssueModel.objects.filter(
            tenant_id=self._tenant_id, project_id=project_id
        ).order_by("-created_at")
        return [IssueMapper.to_entity(m) for m in qs]

    def find_open_issues(self, project_id: UUID) -> List[Issue]:
        qs = IssueModel.objects.filter(
            tenant_id=self._tenant_id,
            project_id=project_id,
        ).exclude(status__in=["resolved", "closed"])
        return [IssueMapper.to_entity(m) for m in qs]

    def find_by_assignee(self, assignee_id: UUID) -> List[Issue]:
        qs = IssueModel.objects.filter(
            tenant_id=self._tenant_id,
            assignee_id=assignee_id,
        ).exclude(status="closed").order_by("-priority", "-created_at")
        return [IssueMapper.to_entity(m) for m in qs]

    def find_critical(self, project_id: UUID) -> List[Issue]:
        qs = IssueModel.objects.filter(
            tenant_id=self._tenant_id,
            project_id=project_id,
        ).filter(
            Q(severity="critical") | Q(priority="critical")
        ).exclude(status__in=["resolved", "closed"])
        return [IssueMapper.to_entity(m) for m in qs]

    def count_by_status(self, project_id: UUID) -> dict:
        qs = IssueModel.objects.filter(
            tenant_id=self._tenant_id, project_id=project_id
        ).values("status").annotate(count=Count("id"))
        return {item["status"]: item["count"] for item in qs}


# ═══════════════════════════════════════════════════
# OBS Node Repository
# ═══════════════════════════════════════════════════

class DjangoOBSNodeRepository(IOBSNodeRepository):
    """Repository گره OBS — ساختار شکست سازمانی."""

    def __init__(self, tenant_id: UUID):
        self._tenant_id = tenant_id

    def save(self, entity: OBSNode) -> OBSNode:
        try:
            model = OBSNodeModel.objects.get(id=entity.id)
            model = OBSNodeMapper.to_model(entity, model)
        except OBSNodeModel.DoesNotExist:
            model = OBSNodeMapper.to_model(entity)
        model.save()
        return OBSNodeMapper.to_entity(model)

    def get_by_id(self, id: UUID) -> Optional[OBSNode]:
        try:
            model = OBSNodeModel.objects.get(id=id, tenant_id=self._tenant_id)
            return OBSNodeMapper.to_entity(model)
        except OBSNodeModel.DoesNotExist:
            return None

    def find(self, **criteria) -> List[OBSNode]:
        qs = OBSNodeModel.objects.filter(
            tenant_id=self._tenant_id, **criteria
        ).order_by("sort_order", "code")
        return [OBSNodeMapper.to_entity(m) for m in qs]

    def delete(self, id: UUID) -> bool:
        deleted, _ = OBSNodeModel.objects.filter(
            id=id, tenant_id=self._tenant_id
        ).delete()
        return deleted > 0

    def find_by_project(self, project_id: UUID, tenant_id: UUID = None) -> List[OBSNode]:
        qs = OBSNodeModel.objects.filter(
            tenant_id=tenant_id or self._tenant_id,
            project_id=project_id,
        ).order_by("sort_order", "code")
        return [OBSNodeMapper.to_entity(m) for m in qs]

    def find_root_nodes(self, project_id: UUID, tenant_id: UUID = None) -> List[OBSNode]:
        qs = OBSNodeModel.objects.filter(
            tenant_id=tenant_id or self._tenant_id,
            project_id=project_id,
            parent__isnull=True,
        ).order_by("sort_order", "code")
        return [OBSNodeMapper.to_entity(m) for m in qs]

    def find_children(self, parent_id: UUID, tenant_id: UUID = None) -> List[OBSNode]:
        qs = OBSNodeModel.objects.filter(
            tenant_id=tenant_id or self._tenant_id,
            parent_id=parent_id,
        ).order_by("sort_order", "code")
        return [OBSNodeMapper.to_entity(m) for m in qs]

    def find_by_code(self, code: str, project_id: UUID, tenant_id: UUID = None):
        try:
            model = OBSNodeModel.objects.get(
                tenant_id=tenant_id or self._tenant_id,
                project_id=project_id,
                code=code,
            )
            return OBSNodeMapper.to_entity(model)
        except OBSNodeModel.DoesNotExist:
            return None

    def find_by_manager(self, manager_id: UUID, tenant_id: UUID = None) -> List[OBSNode]:
        qs = OBSNodeModel.objects.filter(
            tenant_id=tenant_id or self._tenant_id,
            manager_id=manager_id,
            is_active=True,
        ).order_by("sort_order")
        return [OBSNodeMapper.to_entity(m) for m in qs]


# ═══════════════════════════════════════════════════
# RBS Node Repository
# ═══════════════════════════════════════════════════

class DjangoRBSNodeRepository(IRBSNodeRepository):
    """Repository گره RBS — ساختار شکست منابع."""

    def __init__(self, tenant_id: UUID):
        self._tenant_id = tenant_id

    def save(self, entity: RBSNode) -> RBSNode:
        try:
            model = RBSNodeModel.objects.get(id=entity.id)
            model = RBSNodeMapper.to_model(entity, model)
        except RBSNodeModel.DoesNotExist:
            model = RBSNodeMapper.to_model(entity)
        model.save()
        return RBSNodeMapper.to_entity(model)

    def get_by_id(self, id: UUID) -> Optional[RBSNode]:
        try:
            model = RBSNodeModel.objects.get(id=id, tenant_id=self._tenant_id)
            return RBSNodeMapper.to_entity(model)
        except RBSNodeModel.DoesNotExist:
            return None

    def find(self, **criteria) -> List[RBSNode]:
        qs = RBSNodeModel.objects.filter(
            tenant_id=self._tenant_id, **criteria
        ).order_by("sort_order", "code")
        return [RBSNodeMapper.to_entity(m) for m in qs]

    def delete(self, id: UUID) -> bool:
        deleted, _ = RBSNodeModel.objects.filter(
            id=id, tenant_id=self._tenant_id
        ).delete()
        return deleted > 0

    def find_by_project(self, project_id: UUID, tenant_id: UUID = None) -> List[RBSNode]:
        qs = RBSNodeModel.objects.filter(
            tenant_id=tenant_id or self._tenant_id,
            project_id=project_id,
        ).order_by("sort_order", "code")
        return [RBSNodeMapper.to_entity(m) for m in qs]

    def find_root_nodes(self, project_id: UUID, tenant_id: UUID = None) -> List[RBSNode]:
        qs = RBSNodeModel.objects.filter(
            tenant_id=tenant_id or self._tenant_id,
            project_id=project_id,
            parent__isnull=True,
        ).order_by("sort_order", "code")
        return [RBSNodeMapper.to_entity(m) for m in qs]

    def find_children(self, parent_id: UUID, tenant_id: UUID = None) -> List[RBSNode]:
        qs = RBSNodeModel.objects.filter(
            tenant_id=tenant_id or self._tenant_id,
            parent_id=parent_id,
        ).order_by("sort_order", "code")
        return [RBSNodeMapper.to_entity(m) for m in qs]

    def find_by_code(self, code: str, project_id: UUID, tenant_id: UUID = None):
        try:
            model = RBSNodeModel.objects.get(
                tenant_id=tenant_id or self._tenant_id,
                project_id=project_id,
                code=code,
            )
            return RBSNodeMapper.to_entity(model)
        except RBSNodeModel.DoesNotExist:
            return None

    def find_by_resource_type(self, resource_type: str, project_id: UUID, tenant_id: UUID = None) -> List[RBSNode]:
        qs = RBSNodeModel.objects.filter(
            tenant_id=tenant_id or self._tenant_id,
            project_id=project_id,
            resource_type=resource_type,
            is_active=True,
        ).order_by("sort_order")
        return [RBSNodeMapper.to_entity(m) for m in qs]


# ═══════════════════════════════════════════════════
# Quality Plan Repository
# ═══════════════════════════════════════════════════

class DjangoQualityPlanRepository(IQualityPlanRepository):
    """Repository طرح کیفیت."""

    def __init__(self, tenant_id: UUID):
        self._tenant_id = tenant_id

    def save(self, entity: QualityPlan) -> QualityPlan:
        try:
            model = QualityPlanModel.objects.get(id=entity.id)
            model = QualityPlanMapper.to_model(entity, model)
        except QualityPlanModel.DoesNotExist:
            model = QualityPlanMapper.to_model(entity)
        model.save()
        return QualityPlanMapper.to_entity(model)

    def get_by_id(self, id: UUID) -> Optional[QualityPlan]:
        try:
            model = QualityPlanModel.objects.get(id=id, tenant_id=self._tenant_id)
            return QualityPlanMapper.to_entity(model)
        except QualityPlanModel.DoesNotExist:
            return None

    def find(self, **criteria) -> List[QualityPlan]:
        qs = QualityPlanModel.objects.filter(
            tenant_id=self._tenant_id, **criteria
        ).order_by("-created_at")
        return [QualityPlanMapper.to_entity(m) for m in qs]

    def delete(self, id: UUID) -> bool:
        deleted, _ = QualityPlanModel.objects.filter(
            id=id, tenant_id=self._tenant_id
        ).delete()
        return deleted > 0

    def find_by_project(self, project_id: UUID, tenant_id: UUID = None) -> List[QualityPlan]:
        qs = QualityPlanModel.objects.filter(
            tenant_id=tenant_id or self._tenant_id,
            project_id=project_id,
        ).order_by("-created_at")
        return [QualityPlanMapper.to_entity(m) for m in qs]

    def find_active_by_project(self, project_id: UUID, tenant_id: UUID = None) -> List[QualityPlan]:
        qs = QualityPlanModel.objects.filter(
            tenant_id=tenant_id or self._tenant_id,
            project_id=project_id,
            status='active',
        ).order_by("-created_at")
        return [QualityPlanMapper.to_entity(m) for m in qs]


# ═══════════════════════════════════════════════════
# Inspection Repository
# ═══════════════════════════════════════════════════

class DjangoInspectionRepository(IInspectionRepository):
    """Repository بازرسی."""

    def __init__(self, tenant_id: UUID):
        self._tenant_id = tenant_id

    def save(self, entity: Inspection) -> Inspection:
        try:
            model = InspectionModel.objects.get(id=entity.id)
            model = InspectionMapper.to_model(entity, model)
        except InspectionModel.DoesNotExist:
            model = InspectionMapper.to_model(entity)
        model.save()
        return InspectionMapper.to_entity(model)

    def get_by_id(self, id: UUID) -> Optional[Inspection]:
        try:
            model = InspectionModel.objects.get(id=id, tenant_id=self._tenant_id)
            return InspectionMapper.to_entity(model)
        except InspectionModel.DoesNotExist:
            return None

    def find(self, **criteria) -> List[Inspection]:
        qs = InspectionModel.objects.filter(
            tenant_id=self._tenant_id, **criteria
        ).order_by("-created_at")
        return [InspectionMapper.to_entity(m) for m in qs]

    def delete(self, id: UUID) -> bool:
        deleted, _ = InspectionModel.objects.filter(
            id=id, tenant_id=self._tenant_id
        ).delete()
        return deleted > 0

    def find_by_project(self, project_id: UUID, tenant_id: UUID = None) -> List[Inspection]:
        qs = InspectionModel.objects.filter(
            tenant_id=tenant_id or self._tenant_id,
            project_id=project_id,
        ).order_by("-planned_date")
        return [InspectionMapper.to_entity(m) for m in qs]

    def find_by_inspector(self, inspector_id: UUID, tenant_id: UUID = None) -> List[Inspection]:
        qs = InspectionModel.objects.filter(
            tenant_id=tenant_id or self._tenant_id,
            inspector_id=inspector_id,
        ).order_by("-planned_date")
        return [InspectionMapper.to_entity(m) for m in qs]

    def find_by_quality_plan(self, quality_plan_id: UUID, tenant_id: UUID = None) -> List[Inspection]:
        qs = InspectionModel.objects.filter(
            tenant_id=tenant_id or self._tenant_id,
            quality_plan_id=quality_plan_id,
        ).order_by("-planned_date")
        return [InspectionMapper.to_entity(m) for m in qs]

    def find_by_code(self, code: str, project_id: UUID, tenant_id: UUID = None):
        try:
            model = InspectionModel.objects.get(
                tenant_id=tenant_id or self._tenant_id,
                project_id=project_id,
                code=code,
            )
            return InspectionMapper.to_entity(model)
        except InspectionModel.DoesNotExist:
            return None


# ═══════════════════════════════════════════════════
# NCR Repository
# ═══════════════════════════════════════════════════

class DjangoNCRRepository(INCRRepository):
    """Repository عدم انطباق (NCR)."""

    def __init__(self, tenant_id: UUID):
        self._tenant_id = tenant_id

    def save(self, entity: NCR) -> NCR:
        try:
            model = NCRModel.objects.get(id=entity.id)
            model = NCRMapper.to_model(entity, model)
        except NCRModel.DoesNotExist:
            model = NCRMapper.to_model(entity)
        model.save()
        return NCRMapper.to_entity(model)

    def get_by_id(self, id: UUID) -> Optional[NCR]:
        try:
            model = NCRModel.objects.get(id=id, tenant_id=self._tenant_id)
            return NCRMapper.to_entity(model)
        except NCRModel.DoesNotExist:
            return None

    def find(self, **criteria) -> List[NCR]:
        qs = NCRModel.objects.filter(
            tenant_id=self._tenant_id, **criteria
        ).order_by("-created_at")
        return [NCRMapper.to_entity(m) for m in qs]

    def delete(self, id: UUID) -> bool:
        deleted, _ = NCRModel.objects.filter(
            id=id, tenant_id=self._tenant_id
        ).delete()
        return deleted > 0

    def find_by_project(self, project_id: UUID, tenant_id: UUID = None) -> List[NCR]:
        qs = NCRModel.objects.filter(
            tenant_id=tenant_id or self._tenant_id,
            project_id=project_id,
        ).order_by("-created_at")
        return [NCRMapper.to_entity(m) for m in qs]

    def find_by_inspection(self, inspection_id: UUID, tenant_id: UUID = None) -> List[NCR]:
        qs = NCRModel.objects.filter(
            tenant_id=tenant_id or self._tenant_id,
            inspection_id=inspection_id,
        ).order_by("-created_at")
        return [NCRMapper.to_entity(m) for m in qs]

    def find_by_code(self, code: str, project_id: UUID, tenant_id: UUID = None):
        try:
            model = NCRModel.objects.get(
                tenant_id=tenant_id or self._tenant_id,
                project_id=project_id,
                code=code,
            )
            return NCRMapper.to_entity(model)
        except NCRModel.DoesNotExist:
            return None

    def find_open_by_project(self, project_id: UUID, tenant_id: UUID = None) -> List[NCR]:
        qs = NCRModel.objects.filter(
            tenant_id=tenant_id or self._tenant_id,
            project_id=project_id,
        ).exclude(
            status__in=['resolved', 'verified', 'closed'],
        ).order_by("-created_at")
        return [NCRMapper.to_entity(m) for m in qs]


# ═══════════════════════════════════════════════════
# Quality Checklist Repository
# ═══════════════════════════════════════════════════

class DjangoQualityChecklistRepository(IQualityChecklistRepository):
    """Repository چک‌لیست کیفیت."""

    def __init__(self, tenant_id: UUID):
        self._tenant_id = tenant_id

    def save(self, entity: QualityChecklist) -> QualityChecklist:
        try:
            model = QualityChecklistModel.objects.get(id=entity.id)
            model = QualityChecklistMapper.to_model(entity, model)
        except QualityChecklistModel.DoesNotExist:
            model = QualityChecklistMapper.to_model(entity)
        model.save()
        return QualityChecklistMapper.to_entity(model)

    def get_by_id(self, id: UUID) -> Optional[QualityChecklist]:
        try:
            model = QualityChecklistModel.objects.get(id=id, tenant_id=self._tenant_id)
            return QualityChecklistMapper.to_entity(model)
        except QualityChecklistModel.DoesNotExist:
            return None

    def find(self, **criteria) -> List[QualityChecklist]:
        qs = QualityChecklistModel.objects.filter(
            tenant_id=self._tenant_id, **criteria
        ).order_by("-created_at")
        return [QualityChecklistMapper.to_entity(m) for m in qs]

    def delete(self, id: UUID) -> bool:
        deleted, _ = QualityChecklistModel.objects.filter(
            id=id, tenant_id=self._tenant_id
        ).delete()
        return deleted > 0

    def find_by_project(self, project_id: UUID, tenant_id: UUID = None) -> List[QualityChecklist]:
        qs = QualityChecklistModel.objects.filter(
            tenant_id=tenant_id or self._tenant_id,
            project_id=project_id,
        ).order_by("-created_at")
        return [QualityChecklistMapper.to_entity(m) for m in qs]

    def find_by_quality_plan(self, quality_plan_id: UUID, tenant_id: UUID = None) -> List[QualityChecklist]:
        qs = QualityChecklistModel.objects.filter(
            tenant_id=tenant_id or self._tenant_id,
            quality_plan_id=quality_plan_id,
        ).order_by("-created_at")
        return [QualityChecklistMapper.to_entity(m) for m in qs]


# ═══════════════════════════════════════════════════
# Quality Checklist Item Repository
# ═══════════════════════════════════════════════════

class DjangoQualityChecklistItemRepository(IQualityChecklistItemRepository):
    """Repository آیتم چک‌لیست کیفیت."""

    def __init__(self, tenant_id: UUID):
        self._tenant_id = tenant_id

    def save(self, entity: QualityChecklistItem) -> QualityChecklistItem:
        try:
            model = QualityChecklistItemModel.objects.get(id=entity.id)
            model = QualityChecklistItemMapper.to_model(entity, model)
        except QualityChecklistItemModel.DoesNotExist:
            model = QualityChecklistItemMapper.to_model(entity)
        model.save()
        return QualityChecklistItemMapper.to_entity(model)

    def get_by_id(self, id: UUID) -> Optional[QualityChecklistItem]:
        try:
            model = QualityChecklistItemModel.objects.get(id=id, tenant_id=self._tenant_id)
            return QualityChecklistItemMapper.to_entity(model)
        except QualityChecklistItemModel.DoesNotExist:
            return None

    def find(self, **criteria) -> List[QualityChecklistItem]:
        qs = QualityChecklistItemModel.objects.filter(
            tenant_id=self._tenant_id, **criteria
        ).order_by("sort_order")
        return [QualityChecklistItemMapper.to_entity(m) for m in qs]

    def delete(self, id: UUID) -> bool:
        deleted, _ = QualityChecklistItemModel.objects.filter(
            id=id, tenant_id=self._tenant_id
        ).delete()
        return deleted > 0

    def find_by_checklist(self, checklist_id: UUID, tenant_id: UUID = None) -> List[QualityChecklistItem]:
        qs = QualityChecklistItemModel.objects.filter(
            tenant_id=tenant_id or self._tenant_id,
            checklist_id=checklist_id,
        ).order_by("sort_order")
        return [QualityChecklistItemMapper.to_entity(m) for m in qs]


class DjangoProjectDocumentRepository(IProjectDocumentRepository):
    """Repository ارتباط اسناد با پروژه."""

    def __init__(self, tenant_id: UUID):
        self._tenant_id = tenant_id

    def save(self, entity: ProjectDocument) -> ProjectDocument:
        try:
            model = ProjectDocumentModel.objects.get(id=entity.id)
            model = ProjectDocumentMapper.to_model(entity, model)
        except ProjectDocumentModel.DoesNotExist:
            model = ProjectDocumentMapper.to_model(entity)
        model.save()
        return ProjectDocumentMapper.to_entity(model)

    def get_by_id(self, id: UUID) -> Optional[ProjectDocument]:
        try:
            model = ProjectDocumentModel.objects.get(id=id, tenant_id=self._tenant_id)
            return ProjectDocumentMapper.to_entity(model)
        except ProjectDocumentModel.DoesNotExist:
            return None

    def find(self, **criteria) -> List[ProjectDocument]:
        qs = ProjectDocumentModel.objects.filter(
            tenant_id=self._tenant_id, **criteria
        ).order_by("-created_at")
        return [ProjectDocumentMapper.to_entity(m) for m in qs]

    def delete(self, id: UUID) -> bool:
        deleted, _ = ProjectDocumentModel.objects.filter(
            id=id, tenant_id=self._tenant_id
        ).delete()
        return deleted > 0

    def find_by_project(self, project_id: UUID, tenant_id: UUID = None) -> List[ProjectDocument]:
        qs = ProjectDocumentModel.objects.filter(
            tenant_id=tenant_id or self._tenant_id,
            project_id=project_id,
        ).order_by("-created_at")
        return [ProjectDocumentMapper.to_entity(m) for m in qs]

    def find_by_entity(self, entity_type: str, entity_id: UUID, tenant_id: UUID = None) -> List[ProjectDocument]:
        qs = ProjectDocumentModel.objects.filter(
            tenant_id=tenant_id or self._tenant_id,
            entity_type=entity_type,
            entity_id=entity_id,
        ).order_by("-created_at")
        return [ProjectDocumentMapper.to_entity(m) for m in qs]

    def find_by_document(self, document_id: UUID, tenant_id: UUID = None) -> Optional[ProjectDocument]:
        try:
            model = ProjectDocumentModel.objects.get(
                tenant_id=tenant_id or self._tenant_id,
                document_id=document_id,
            )
            return ProjectDocumentMapper.to_entity(model)
        except ProjectDocumentModel.DoesNotExist:
            return None
