"""
PM API Views.

ViewSetهای DRF ماژول مدیریت پروژه.
"""
from rest_framework import viewsets, status, filters
from rest_framework.decorators import action
from rest_framework.permissions import IsAuthenticated
from rest_framework.response import Response
from django_filters.rest_framework import DjangoFilterBackend
from django.db.models import Prefetch

from ...infrastructure.persistence.models import (
    ProjectModel,
    TaskModel,
    DependencyModel,
    ResourceModel,
    ResourceAssignmentModel,
    BudgetModel,
    CostEntryModel,
    RiskModel,
    BaselineModel,
    CalendarModel,
    HolidayModel,
    ChangeRequestModel,
    TimesheetModel,
    ProjectMemberModel,
    CommentModel,
    ActivityLogModel,
    TaskLabelModel,
    TaskLabelAssignmentModel,
    TaskAttachmentModel,
    TaskChecklistModel,
    TaskChecklistItemModel,
    RACIAssignmentModel,
    TaskAssignmentModel,
    ActivityCodeTypeModel,
    ActivityCodeValueModel,
    TaskActivityCodeModel,
    UDFDefinitionModel,
    UDFValueModel,
    ResourceSkillModel,
    ResourceAvailabilityModel,
    ResourceCostRateModel,
    CostAccountModel,
    FundingSourceModel,
    CashFlowEntryModel,
    ScheduleHealthSnapshotModel,
    ProjectTemplateModel,
    IssueModel,
    OBSNodeModel,
    RBSNodeModel,
    QualityPlanModel,
    InspectionModel,
    NCRModel,
    QualityChecklistModel,
    QualityChecklistItemModel,
    ProjectDocumentModel,
)
from .serializers import (
    ProjectListSerializer, ProjectDetailSerializer,
    ProjectCreateSerializer, ProjectStatusSerializer,
    TaskListSerializer, TaskDetailSerializer,
    TaskCreateSerializer, TaskUpdateSerializer,
    TaskStatusSerializer, TaskMoveSerializer, TaskProgressSerializer,
    DependencySerializer, DependencyCreateSerializer,
    ResourceListSerializer, ResourceDetailSerializer, ResourceCreateSerializer,
    AssignmentSerializer, AssignmentCreateSerializer,
    BudgetSerializer, BudgetCreateSerializer,
    CostEntrySerializer, CostEntryCreateSerializer,
    RiskListSerializer, RiskDetailSerializer, RiskCreateSerializer,
    BaselineSerializer, BaselineCreateSerializer,
    CalendarSerializer, HolidaySerializer,
    ChangeRequestListSerializer, ChangeRequestDetailSerializer,
    ChangeRequestCreateSerializer,
    TimesheetSerializer, TimesheetCreateSerializer,
    MemberSerializer, MemberCreateSerializer,
    CommentSerializer, CommentCreateSerializer,
    ActivityLogSerializer,
    TaskLabelSerializer, TaskLabelCreateSerializer,
    TaskLabelAssignmentSerializer, TaskLabelAssignmentCreateSerializer,
    TaskAttachmentSerializer, TaskAttachmentCreateSerializer,
    TaskChecklistSerializer, TaskChecklistCreateSerializer,
    TaskChecklistItemSerializer, TaskChecklistItemCreateSerializer,
    RACIAssignmentSerializer, RACIAssignmentCreateSerializer,
    KanbanCardSerializer,
    # Advanced serializers
    TaskAssignmentSerializer, TaskAssignmentCreateSerializer,
    ActivityCodeTypeSerializer, ActivityCodeTypeCreateSerializer,
    ActivityCodeValueSerializer, ActivityCodeValueCreateSerializer,
    TaskActivityCodeSerializer, TaskActivityCodeCreateSerializer,
    UDFDefinitionSerializer, UDFDefinitionCreateSerializer,
    UDFValueSerializer, UDFValueCreateSerializer,
    ResourceSkillSerializer, ResourceSkillCreateSerializer,
    ResourceAvailabilitySerializer, ResourceAvailabilityCreateSerializer,
    ResourceCostRateSerializer, ResourceCostRateCreateSerializer,
    CostAccountSerializer, CostAccountCreateSerializer,
    FundingSourceSerializer, FundingSourceCreateSerializer,
    CashFlowEntrySerializer, CashFlowEntryCreateSerializer,
    ScheduleHealthSnapshotSerializer, ScheduleHealthRunSerializer,
    ProjectTemplateListSerializer, ProjectTemplateDetailSerializer,
    ProjectTemplateCreateSerializer, SaveProjectAsTemplateSerializer,
    MonteCarloRunSerializer, MonteCarloResultSerializer,
    ResourceLevelingRunSerializer, ResourceLevelingResultSerializer,
    IssueListSerializer, IssueDetailSerializer, IssueCreateSerializer,
    IssueAssignSerializer, IssueResolveSerializer,
    OBSNodeListSerializer, OBSNodeDetailSerializer,
    OBSNodeCreateSerializer, OBSNodeTreeSerializer,
    RBSNodeListSerializer, RBSNodeDetailSerializer,
    RBSNodeCreateSerializer, RBSNodeTreeSerializer,
    QualityPlanListSerializer, QualityPlanDetailSerializer, QualityPlanCreateSerializer,
    InspectionListSerializer, InspectionDetailSerializer, InspectionCreateSerializer,
    NCRListSerializer, NCRDetailSerializer, NCRCreateSerializer,
    QualityChecklistListSerializer, QualityChecklistDetailSerializer,
    QualityChecklistCreateSerializer,
    QualityChecklistItemSerializer, QualityChecklistItemCreateSerializer,
    QualityChecklistItemUpdateSerializer,
    ProjectDocumentListSerializer, ProjectDocumentCreateSerializer,
)
from .permissions import (
    ProjectPermission, TaskPermission,
    ResourcePermission, CostPermission,
    RiskPermission, CalendarPermission,
    IssuePermission,
    OBSPermission,
    RBSPermission,
    QualityPlanPermission,
    InspectionPermission,
    NCRPermission,
    QualityChecklistPermission,
    ProjectDocumentPermission,
)


class TenantFilterMixin:
    """Mixin — فیلتر خودکار queryset بر اساس tenant."""

    def get_queryset(self):
        qs = super().get_queryset()
        if hasattr(self.request, "tenant"):
            return qs.filter(tenant=self.request.tenant)
        return qs

    def perform_create(self, serializer):
        kwargs = {}
        if hasattr(self.request, "tenant"):
            kwargs["tenant"] = self.request.tenant
        serializer.save(**kwargs)


# ═══════════════════════════════════════════════════
# Helper Functions
# ═══════════════════════════════════════════════════

def _check_fs_dependencies_satisfied(task_id, tenant=None):
    """
    بررسی وابستگی‌های FS برای تسک.
    برمی‌گرداند (satisfied: bool, message: str)
    """
    deps = DependencyModel.objects.filter(successor_id=task_id, dependency_type="FS")
    if tenant:
        deps = deps.filter(tenant=tenant)
    deps = deps.select_related("predecessor")
    for dep in deps:
        pred = dep.predecessor
        if pred.status != "completed":
            return False, f"تسک پیش‌نیاز «{pred.title}» هنوز تکمیل نشده است"
    return True, ""


def _recalculate_project_progress(project_id, tenant=None):
    """
    محاسبه میانگین وزنی پیشرفت پروژه بر اساس duration تسک‌ها.
    weight = task.duration, value = task.progress
    project_progress = sum(duration_i * progress_i) / sum(duration_i)
    """
    from django.db.models import Sum, F
    qs = TaskModel.objects.filter(project_id=project_id).exclude(
        task_type="summary"
    ).exclude(status="cancelled")
    if tenant:
        qs = qs.filter(tenant=tenant)

    total_duration = qs.aggregate(total=Sum("duration"))["total"] or 0
    if total_duration == 0:
        return 0

    weighted_sum = 0
    for task in qs.values("duration", "progress"):
        weighted_sum += (task["duration"] or 1) * (task["progress"] or 0)

    project_progress = round(weighted_sum / total_duration)
    ProjectModel.objects.filter(id=project_id).update(progress=project_progress)
    return project_progress


def _log_activity(project_id, task_id, user, action, entity_type, entity_id,
                   description="", changes=None, tenant=None, ip_address=None):
    """ثبت لاگ فعالیت."""
    import uuid as _uuid
    kwargs = dict(
        id=_uuid.uuid4(),
        project_id=project_id,
        task_id=task_id,
        user=user,
        action=action,
        entity_type=entity_type,
        entity_id=entity_id,
        description=description,
        changes=changes or {},
        ip_address=ip_address,
    )
    if tenant:
        kwargs["tenant"] = tenant
    ActivityLogModel.objects.create(**kwargs)


# ═══════════════════════════════════════════════════
# Project ViewSet
# ═══════════════════════════════════════════════════

class ProjectViewSet(TenantFilterMixin, viewsets.ModelViewSet):
    """API مدیریت پروژه‌ها."""
    queryset = ProjectModel.objects.all()
    permission_classes = [IsAuthenticated, ProjectPermission]
    filter_backends = [DjangoFilterBackend, filters.SearchFilter, filters.OrderingFilter]
    filterset_fields = ["status", "priority", "project_type", "company", "manager"]
    search_fields = ["code", "title", "description"]
    ordering_fields = ["created_at", "planned_start", "progress", "title"]
    ordering = ["-created_at"]

    def get_serializer_class(self):
        if self.action == "list":
            return ProjectListSerializer
        if self.action == "create":
            return ProjectCreateSerializer
        if self.action == "change_status":
            return ProjectStatusSerializer
        return ProjectDetailSerializer

    def perform_create(self, serializer):
        kwargs = {}
        if hasattr(self.request, "tenant"):
            kwargs["tenant"] = self.request.tenant
        kwargs["created_by"] = self.request.user
        serializer.save(**kwargs)

    def perform_update(self, serializer):
        serializer.save(updated_by=self.request.user)

    @action(detail=True, methods=["post"], url_path="change-status")
    def change_status(self, request, pk=None):
        """تغییر وضعیت پروژه."""
        project = self.get_object()
        serializer = ProjectStatusSerializer(data=request.data)
        serializer.is_valid(raise_exception=True)
        new_status = serializer.validated_data["status"]

        valid_transitions = {
            "draft": ["planning", "cancelled"],
            "planning": ["active", "cancelled"],
            "active": ["on_hold", "completed", "cancelled"],
            "on_hold": ["active", "cancelled"],
            "completed": ["archived"],
            "cancelled": ["archived"],
        }
        allowed = valid_transitions.get(project.status, [])
        if new_status not in allowed:
            return Response(
                {"error": {"code": "INVALID_TRANSITION",
                           "message": f"انتقال از «{project.get_status_display()}» به «{new_status}» مجاز نیست."}},
                status=status.HTTP_422_UNPROCESSABLE_ENTITY,
            )

        from datetime import date
        project.status = new_status
        if new_status == "active" and not project.actual_start:
            project.actual_start = date.today()
        elif new_status == "completed":
            project.actual_end = date.today()
            project.progress = 100
        project.updated_by = request.user
        project.save()

        return Response(ProjectDetailSerializer(project).data)

    @action(detail=True, methods=["get"])
    def dashboard(self, request, pk=None):
        """داشبورد پروژه."""
        project = self.get_object()
        tasks = TaskModel.objects.filter(project=project)
        total = tasks.count()
        completed = tasks.filter(status="completed").count()

        from datetime import date
        overdue = tasks.filter(
            planned_end__lt=date.today()
        ).exclude(status__in=["completed", "cancelled"]).count()

        milestones = tasks.filter(task_type="milestone").values(
            "id", "title", "planned_end", "status"
        )

        return Response({
            "data": {
                "project": ProjectDetailSerializer(project).data,
                "task_summary": {
                    "total": total,
                    "completed": completed,
                    "in_progress": tasks.filter(status="in_progress").count(),
                    "overdue": overdue,
                },
                "milestones": list(milestones),
            }
        })

    @action(detail=False, methods=["get"])
    def statistics(self, request):
        """آمار کلی پروژه‌ها."""
        qs = self.get_queryset()
        from django.db.models import Avg

        return Response({"data": {
            "total_count": qs.count(),
            "portfolio_count": qs.filter(project_type="portfolio").count(),
            "program_count": qs.filter(project_type="program").count(),
            "project_count": qs.filter(project_type="project").count(),
            "sub_project_count": qs.filter(project_type="sub_project").count(),
            "active_count": qs.filter(status="active").count(),
            "planning_count": qs.filter(status="planning").count(),
            "completed_count": qs.filter(status="completed").count(),
            "on_hold_count": qs.filter(status="on_hold").count(),
            "cancelled_count": qs.filter(status="cancelled").count(),
            "avg_progress": round(qs.aggregate(avg=Avg("progress"))["avg"] or 0),
        }})

    @action(detail=True, methods=["post"], url_path="recalculate-progress")
    def recalculate_progress(self, request, pk=None):
        """محاسبه مجدد پیشرفت پروژه بر اساس میانگین وزنی تسک‌ها."""
        project = self.get_object()
        tenant = getattr(request, "tenant", None)
        new_progress = _recalculate_project_progress(project.id, tenant)
        project.refresh_from_db()
        return Response({
            "data": {
                "project_id": str(project.id),
                "progress": new_progress,
            }
        })


# ═══════════════════════════════════════════════════
# Task ViewSet
# ═══════════════════════════════════════════════════

class TaskViewSet(TenantFilterMixin, viewsets.ModelViewSet):
    """API مدیریت تسک‌ها."""
    queryset = TaskModel.objects.select_related("project", "assignee").prefetch_related(
        Prefetch(
            "raci_assignments",
            queryset=RACIAssignmentModel.objects.select_related("user"),
            to_attr="_prefetched_raci",
        )
    ).all()
    permission_classes = [IsAuthenticated, TaskPermission]
    filter_backends = [DjangoFilterBackend, filters.SearchFilter, filters.OrderingFilter]
    filterset_fields = ["project", "status", "task_type", "assignee", "parent", "is_critical"]
    search_fields = ["code", "title", "description"]
    ordering_fields = ["sort_order", "created_at", "planned_start", "progress"]
    ordering = ["sort_order", "created_at"]

    def perform_create(self, serializer):
        """ایجاد تسک + تولید خودکار کد WBS."""
        kwargs = {}
        if hasattr(self.request, "tenant"):
            kwargs["tenant"] = self.request.tenant
        task = serializer.save(**kwargs)

        # Auto-generate WBS codes for the whole project
        from ...domain.services.scheduling import WBSService
        from ...domain.entities.task import Task as TaskEntity

        project_tasks = self.get_queryset().filter(
            project_id=task.project_id,
        ).order_by("sort_order", "created_at")

        task_entities = []
        for t in project_tasks:
            entity = TaskEntity(
                id=t.id, tenant_id=t.tenant_id,
                project_id=t.project_id, code=t.code, title=t.title,
                parent_id=t.parent_id, sort_order=t.sort_order,
            )
            task_entities.append(entity)

        wbs_service = WBSService()
        wbs_service.generate_wbs_codes(task_entities)

        code_map = {e.id: e.code for e in task_entities}
        for t in project_tasks:
            new_code = code_map.get(t.id)
            if new_code and t.code != new_code:
                t.code = new_code
                t.save(update_fields=["code"])

    def perform_update(self, serializer):
        """به‌روزرسانی تسک + بازتولید کد WBS در صورت تغییر والد."""
        old_parent = self.get_object().parent_id
        task = serializer.save()
        if task.parent_id != old_parent:
            from ...domain.services.scheduling import WBSService
            from ...domain.entities.task import Task as TaskEntity

            project_tasks = self.get_queryset().filter(
                project_id=task.project_id,
            ).order_by("sort_order", "created_at")

            task_entities = []
            for t in project_tasks:
                entity = TaskEntity(
                    id=t.id, tenant_id=t.tenant_id,
                    project_id=t.project_id, code=t.code, title=t.title,
                    parent_id=t.parent_id, sort_order=t.sort_order,
                )
                task_entities.append(entity)

            wbs_service = WBSService()
            wbs_service.generate_wbs_codes(task_entities)

            code_map = {e.id: e.code for e in task_entities}
            for t in project_tasks:
                new_code = code_map.get(t.id)
                if new_code and t.code != new_code:
                    t.code = new_code
                    t.save(update_fields=["code"])

    def get_serializer_class(self):
        if self.action == "list":
            return TaskListSerializer
        if self.action == "create":
            return TaskCreateSerializer
        if self.action in ("update", "partial_update"):
            return TaskUpdateSerializer
        if self.action == "change_status":
            return TaskStatusSerializer
        if self.action == "move":
            return TaskMoveSerializer
        if self.action == "update_progress":
            return TaskProgressSerializer
        return TaskDetailSerializer

    @action(detail=True, methods=["post"], url_path="change-status")
    def change_status(self, request, pk=None):
        """تغییر وضعیت تسک."""
        task = self.get_object()
        serializer = TaskStatusSerializer(data=request.data)
        serializer.is_valid(raise_exception=True)
        new_status = serializer.validated_data["status"]

        valid_transitions = {
            "not_started": ["in_progress", "cancelled"],
            "in_progress": ["completed", "on_hold", "cancelled"],
            "on_hold": ["in_progress", "cancelled"],
        }
        allowed = valid_transitions.get(task.status, [])
        if new_status not in allowed:
            return Response(
                {"error": {"code": "INVALID_TRANSITION",
                           "message": f"انتقال وضعیت مجاز نیست."}},
                status=status.HTTP_422_UNPROCESSABLE_ENTITY,
            )

        # بررسی وابستگی FS قبل از شروع/تکمیل
        if new_status in ("in_progress", "completed"):
            tenant = getattr(request, "tenant", None)
            satisfied, msg = _check_fs_dependencies_satisfied(task.id, tenant)
            if not satisfied:
                return Response(
                    {"error": {"code": "DEPENDENCY_NOT_SATISFIED", "message": msg}},
                    status=status.HTTP_422_UNPROCESSABLE_ENTITY,
                )

        from datetime import date
        old_status = task.status
        task.status = new_status
        if new_status == "in_progress" and not task.actual_start:
            task.actual_start = date.today()
        elif new_status == "completed":
            task.actual_end = date.today()
            task.progress = 100
        task.save()

        # ثبت لاگ فعالیت
        _log_activity(
            project_id=task.project_id, task_id=task.id,
            user=request.user, action="status_changed",
            entity_type="task", entity_id=task.id,
            description=f"وضعیت تسک از «{old_status}» به «{new_status}» تغییر کرد",
            changes={"old_status": old_status, "new_status": new_status},
            tenant=getattr(request, "tenant", None),
        )

        # به‌روزرسانی پیشرفت پروژه
        _recalculate_project_progress(
            task.project_id, getattr(request, "tenant", None)
        )

        return Response(TaskDetailSerializer(task).data)

    @action(detail=True, methods=["post"])
    def move(self, request, pk=None):
        """جابجایی تسک (والد/ترتیب) و بازشماره‌گذاری WBS."""
        task = self.get_object()
        serializer = TaskMoveSerializer(data=request.data)
        serializer.is_valid(raise_exception=True)
        if "parent_id" in serializer.validated_data:
            task.parent_id = serializer.validated_data["parent_id"]
        if "sort_order" in serializer.validated_data:
            task.sort_order = serializer.validated_data["sort_order"]
        task.save()

        # Auto-recalculate WBS codes for the project
        project_tasks = self.get_queryset().filter(
            project_id=task.project_id
        ).order_by("sort_order", "created_at")

        from ...domain.services.scheduling import WBSService
        from ...domain.entities.task import Task as TaskEntity

        task_entities = []
        for t in project_tasks:
            entity = TaskEntity(
                id=t.id, tenant_id=t.tenant_id,
                project_id=t.project_id, code=t.code, title=t.title,
                parent_id=t.parent_id, sort_order=t.sort_order,
            )
            task_entities.append(entity)

        wbs_service = WBSService()
        wbs_service.generate_wbs_codes(task_entities)

        code_map = {e.id: e.code for e in task_entities}
        for t in project_tasks:
            new_code = code_map.get(t.id)
            if new_code and t.code != new_code:
                t.code = new_code
                t.save(update_fields=["code"])

        # Refresh task object with updated code
        task.refresh_from_db()
        return Response(TaskDetailSerializer(task).data)

    @action(detail=True, methods=["post"], url_path="update-progress")
    def update_progress(self, request, pk=None):
        """ثبت پیشرفت تسک با اعتبارسنجی وابستگی."""
        task = self.get_object()
        serializer = TaskProgressSerializer(data=request.data)
        serializer.is_valid(raise_exception=True)
        new_progress = serializer.validated_data["progress"]
        notes = serializer.validated_data.get("notes", "")

        # بررسی وابستگی FS
        if new_progress > 0:
            tenant = getattr(request, "tenant", None)
            satisfied, msg = _check_fs_dependencies_satisfied(task.id, tenant)
            if not satisfied:
                return Response(
                    {"error": {"code": "DEPENDENCY_NOT_SATISFIED", "message": msg}},
                    status=status.HTTP_422_UNPROCESSABLE_ENTITY,
                )

        from datetime import date
        old_progress = task.progress
        task.progress = new_progress

        # به‌روزرسانی وضعیت بر اساس پیشرفت
        if new_progress > 0 and task.status == "not_started":
            task.status = "in_progress"
            task.actual_start = date.today()
        if new_progress >= 100 and task.status != "completed":
            task.status = "completed"
            task.actual_end = date.today()
            task.progress = 100
        task.save()

        # ثبت لاگ فعالیت
        _log_activity(
            project_id=task.project_id, task_id=task.id,
            user=request.user, action="progress_updated",
            entity_type="task", entity_id=task.id,
            description=f"پیشرفت تسک از {old_progress}% به {new_progress}% تغییر کرد",
            changes={"old_progress": old_progress, "new_progress": new_progress, "notes": notes},
            tenant=getattr(request, "tenant", None),
        )

        # به‌روزرسانی پیشرفت پروژه
        _recalculate_project_progress(
            task.project_id, getattr(request, "tenant", None)
        )

        return Response(TaskDetailSerializer(task).data)

    @action(detail=False, methods=["get"], url_path="my-tasks",
            permission_classes=[IsAuthenticated])
    def my_tasks(self, request):
        """کارتابل کاربر — تسک‌هایی که به کاربر RACI تخصیص داده شده."""
        user = request.user
        tenant = getattr(request, "tenant", None)

        # دریافت RACI assignments کاربر
        raci_qs = RACIAssignmentModel.objects.filter(
            user=user, task__isnull=False,
        ).select_related("task", "task__project")
        if tenant:
            raci_qs = raci_qs.filter(tenant=tenant)

        # Build task data with RACI roles
        task_map = {}
        for raci in raci_qs:
            task = raci.task
            tid = str(task.id)
            if tid not in task_map:
                task_data = TaskListSerializer(task).data
                task_data["raci_roles"] = []
                task_data["project_title"] = task.project.title if task.project else ""
                task_map[tid] = task_data
            task_map[tid]["raci_roles"].append(raci.role)

        # همچنین تسک‌هایی که مستقیماً assign شده‌اند
        assigned_qs = self.get_queryset().filter(assignee=user)
        for task in assigned_qs:
            tid = str(task.id)
            if tid not in task_map:
                task_data = TaskListSerializer(task).data
                task_data["raci_roles"] = []
                task_data["project_title"] = task.project.title if hasattr(task, 'project') and task.project else ""
                task_map[tid] = task_data

        return Response({"data": list(task_map.values())})

    @action(detail=True, methods=["get"], url_path="my-raci-role",
            permission_classes=[IsAuthenticated])
    def my_raci_role(self, request, pk=None):
        """نقش RACI کاربر جاری برای تسک مشخص."""
        task = self.get_object()
        user = request.user
        roles = list(
            RACIAssignmentModel.objects.filter(task=task, user=user)
            .values_list("role", flat=True)
        )
        # اولویت نقش: R > A > C > I
        priority_role = None
        for r in ["R", "A", "C", "I"]:
            if r in roles:
                priority_role = r
                break
        # اگر assignee مستقیم باشد ولی RACI نداشته باشد → R فرض شود
        if not priority_role and task.assignee_id == user.id:
            priority_role = "R"
        return Response({
            "data": {
                "task_id": str(task.id),
                "roles": roles,
                "primary_role": priority_role,
            }
        })

    @action(detail=True, methods=["post"], url_path="approve",
            permission_classes=[IsAuthenticated])
    def approve_task(self, request, pk=None):
        """تأیید/رد تسک توسط نقش Accountable (A)."""
        task = self.get_object()
        user = request.user

        # بررسی نقش A
        has_a_role = RACIAssignmentModel.objects.filter(
            task=task, user=user, role="A"
        ).exists()
        if not has_a_role:
            return Response(
                {"error": {"code": "NOT_ACCOUNTABLE", "message": "شما نقش پاسخگو (A) برای این تسک ندارید."}},
                status=status.HTTP_403_FORBIDDEN,
            )

        decision = request.data.get("decision")  # "approve" or "reject"
        comment = request.data.get("comment", "")

        if decision not in ("approve", "reject"):
            return Response(
                {"error": {"code": "INVALID_DECISION", "message": "تصمیم باید approve یا reject باشد."}},
                status=status.HTTP_400_BAD_REQUEST,
            )

        from datetime import date
        if decision == "approve":
            if task.status == "completed":
                return Response(
                    {"error": {"code": "ALREADY_COMPLETED", "message": "این تسک قبلاً تکمیل شده است."}},
                    status=status.HTTP_422_UNPROCESSABLE_ENTITY,
                )
            old_status = task.status
            task.status = "completed"
            task.progress = 100
            task.actual_end = date.today()
            task.save()
            action_label = "تأیید"
        else:
            old_status = task.status
            task.status = "in_progress"
            task.progress = max(task.progress - 10, 0) if task.progress > 0 else 0
            task.save()
            action_label = "رد"

        _log_activity(
            project_id=task.project_id, task_id=task.id,
            user=user, action="task_approved" if decision == "approve" else "task_rejected",
            entity_type="task", entity_id=task.id,
            description=f"تسک توسط {user.get_full_name() or user.email} {action_label} شد. {comment}",
            changes={"decision": decision, "comment": comment, "old_status": old_status},
            tenant=getattr(request, "tenant", None),
        )
        _recalculate_project_progress(task.project_id, getattr(request, "tenant", None))
        return Response(TaskDetailSerializer(task).data)

    @action(detail=True, methods=["post"], url_path="consult",
            permission_classes=[IsAuthenticated])
    def add_consultation(self, request, pk=None):
        """ثبت نظر مشاوره‌ای توسط نقش Consulted (C)."""
        task = self.get_object()
        user = request.user

        has_c_role = RACIAssignmentModel.objects.filter(
            task=task, user=user, role="C"
        ).exists()
        if not has_c_role:
            return Response(
                {"error": {"code": "NOT_CONSULTED", "message": "شما نقش مشاوره (C) برای این تسک ندارید."}},
                status=status.HTTP_403_FORBIDDEN,
            )

        comment = request.data.get("comment", "").strip()
        if not comment:
            return Response(
                {"error": {"code": "COMMENT_REQUIRED", "message": "نظر مشاوره الزامی است."}},
                status=status.HTTP_400_BAD_REQUEST,
            )

        _log_activity(
            project_id=task.project_id, task_id=task.id,
            user=user, action="consultation_added",
            entity_type="task", entity_id=task.id,
            description=f"نظر مشاوره توسط {user.get_full_name() or user.email}: {comment}",
            changes={"comment": comment, "role": "C"},
            tenant=getattr(request, "tenant", None),
        )
        return Response({"data": {"message": "نظر مشاوره با موفقیت ثبت شد.", "task_id": str(task.id)}})

    @action(detail=True, methods=["post"], url_path="acknowledge",
            permission_classes=[IsAuthenticated])
    def acknowledge_task(self, request, pk=None):
        """ثبت اطلاع توسط نقش Informed (I)."""
        task = self.get_object()
        user = request.user

        has_i_role = RACIAssignmentModel.objects.filter(
            task=task, user=user, role="I"
        ).exists()
        if not has_i_role:
            return Response(
                {"error": {"code": "NOT_INFORMED", "message": "شما نقش مطلع (I) برای این تسک ندارید."}},
                status=status.HTTP_403_FORBIDDEN,
            )

        _log_activity(
            project_id=task.project_id, task_id=task.id,
            user=user, action="task_acknowledged",
            entity_type="task", entity_id=task.id,
            description=f"اطلاع‌رسانی تسک توسط {user.get_full_name() or user.email} تأیید شد.",
            changes={"role": "I"},
            tenant=getattr(request, "tenant", None),
        )
        return Response({"data": {"message": "مطلع شدم — با موفقیت ثبت شد.", "task_id": str(task.id)}})

    def perform_destroy(self, instance):
        """بررسی وابستگی قبل از حذف تسک."""
        # آیا این تسک به عنوان پیش‌نیاز استفاده شده؟
        successor_deps = DependencyModel.objects.filter(predecessor=instance)
        if successor_deps.exists():
            successor_titles = ", ".join(
                [d.successor.title for d in successor_deps.select_related("successor")]
            )
            from rest_framework.exceptions import ValidationError
            raise ValidationError({
                "error": {
                    "code": "TASK_HAS_DEPENDENTS",
                    "message": f"این تسک به عنوان پیش‌نیاز تسک‌های دیگر استفاده شده است",
                    "details": {
                        "dependent_tasks": successor_titles,
                        "suggestion": "ابتدا وابستگی‌ها را حذف کنید یا تسک را غیرفعال کنید",
                    },
                }
            })
        instance.delete()

        # Auto-recalculate WBS codes for the project
        project_tasks = self.get_queryset().filter(
            project_id=task.project_id
        ).order_by("sort_order", "created_at")

        from ...domain.services.scheduling import WBSService
        from ...domain.entities.task import Task as TaskEntity

        task_entities = []
        for t in project_tasks:
            entity = TaskEntity(
                id=t.id, tenant_id=t.tenant_id,
                project_id=t.project_id, code=t.code, title=t.title,
                parent_id=t.parent_id, sort_order=t.sort_order,
            )
            task_entities.append(entity)

        wbs_service = WBSService()
        wbs_service.generate_wbs_codes(task_entities)

        code_map = {e.id: e.code for e in task_entities}
        for t in project_tasks:
            new_code = code_map.get(t.id)
            if new_code and t.code != new_code:
                t.code = new_code
                t.save(update_fields=["code"])

        # Refresh task object with updated code
        task.refresh_from_db()
        return Response(TaskDetailSerializer(task).data)

    @action(detail=False, methods=["get"], url_path="tree")
    def tree(self, request):
        """ساختار درختی تسک‌ها — پشتیبانی از پروژه یا برنامه."""
        project_id = request.query_params.get("project")
        program_id = request.query_params.get("program")

        if program_id:
            # حالت برنامه: نمایش پروژه‌ها و تسک‌های زیرمجموعه
            projects = ProjectModel.objects.filter(
                parent_id=program_id
            ).order_by("title")
            if not projects.exists():
                # اگر زیرپروژه نداشت، شاید خودش پروژه باشد
                projects = ProjectModel.objects.filter(id=program_id)

            result = []
            for proj in projects:
                tasks = self.get_queryset().filter(
                    project=proj
                ).order_by("sort_order", "created_at")
                serialized = TaskListSerializer(tasks, many=True).data
                task_tree = self._build_task_tree(serialized)
                project_node = {
                    "id": str(proj.id),
                    "title": proj.title,
                    "code": proj.code or "",
                    "task_type": "project",
                    "status": proj.status,
                    "progress": proj.progress or 0,
                    "planned_start": str(proj.planned_start) if proj.planned_start else None,
                    "planned_end": str(proj.planned_end) if proj.planned_end else None,
                    "duration": None,
                    "is_critical": False,
                    "is_milestone": False,
                    "parent": None,
                    "sort_order": 0,
                    "children": task_tree,
                }
                result.append(project_node)
            return Response({"data": result})

        elif project_id:
            tasks = self.get_queryset().filter(
                project_id=project_id
            ).order_by("sort_order", "created_at")
            serialized = TaskListSerializer(tasks, many=True).data
            roots = self._build_task_tree(serialized)
            return Response({"data": roots})

        else:
            return Response(
                {"error": {"message": "پارامتر project یا program الزامی است."}},
                status=status.HTTP_400_BAD_REQUEST,
            )

    @staticmethod
    def _build_task_tree(serialized):
        """ساخت درخت تسک از لیست سریالایز شده."""
        task_map = {str(t["id"]): {**t, "children": []} for t in serialized}
        roots = []
        for t in serialized:
            parent = str(t["parent"]) if t["parent"] else None
            if parent and parent in task_map:
                task_map[parent]["children"].append(task_map[str(t["id"])])
            else:
                roots.append(task_map[str(t["id"])])
        return roots

    @action(detail=False, methods=["post"], url_path="calculate-cpm")
    def calculate_cpm(self, request):
        """محاسبه مسیر بحرانی."""
        project_id = request.data.get("project")
        if not project_id:
            return Response(
                {"error": {"message": "پارامتر project الزامی است."}},
                status=status.HTTP_400_BAD_REQUEST,
            )

        from ...application.services.project_task_services import TaskService
        tenant = getattr(request, "tenant", None)
        tenant_id = tenant.id if tenant else None
        if tenant_id:
            svc = TaskService(tenant_id)
            critical = svc.calculate_critical_path(project_id)
            return Response({
                "data": {
                    "critical_count": len(critical),
                    "critical_tasks": [
                        {"id": str(t.id), "code": t.code, "title": t.title}
                        for t in critical
                    ],
                }
            })
        return Response({"data": {"critical_count": 0, "critical_tasks": []}})

    @action(detail=False, methods=["post"], url_path="recalculate-wbs")
    def recalculate_wbs(self, request):
        """بازشماره‌گذاری کدهای WBS پروژه."""
        project_id = request.data.get("project")
        if not project_id:
            return Response(
                {"error": {"message": "پارامتر project الزامی است."}},
                status=status.HTTP_400_BAD_REQUEST,
            )

        tasks = self.get_queryset().filter(
            project_id=project_id
        ).order_by("sort_order", "created_at")

        # Build in-memory tree and assign WBS codes
        from ...domain.services.scheduling import WBSService
        from ...domain.entities.task import Task as TaskEntity

        task_entities = []
        for t in tasks:
            entity = TaskEntity(
                id=t.id, tenant_id=t.tenant_id,
                project_id=t.project_id, code=t.code, title=t.title,
                parent_id=t.parent_id, sort_order=t.sort_order,
            )
            task_entities.append(entity)

        wbs_service = WBSService()
        wbs_service.generate_wbs_codes(task_entities)

        # Bulk update codes
        code_map = {e.id: e.code for e in task_entities}
        updated = 0
        for t in tasks:
            new_code = code_map.get(t.id)
            if new_code and t.code != new_code:
                t.code = new_code
                t.save(update_fields=["code"])
                updated += 1

        return Response({
            "data": {
                "message": f"کدهای WBS {updated} تسک بازشماره‌گذاری شد.",
                "updated_count": updated,
            }
        })


# ═══════════════════════════════════════════════════
# Dependency ViewSet
# ═══════════════════════════════════════════════════

class DependencyViewSet(TenantFilterMixin, viewsets.ModelViewSet):
    """API مدیریت وابستگی‌ها."""
    queryset = DependencyModel.objects.select_related(
        "predecessor", "successor",
    ).all()
    permission_classes = [IsAuthenticated, TaskPermission]
    filter_backends = [DjangoFilterBackend]
    filterset_fields = ["project", "predecessor", "successor"]

    def get_serializer_class(self):
        if self.action == "create":
            return DependencyCreateSerializer
        return DependencySerializer


# ═══════════════════════════════════════════════════
# Resource ViewSet
# ═══════════════════════════════════════════════════

class ResourceViewSet(TenantFilterMixin, viewsets.ModelViewSet):
    """API مدیریت منابع."""
    queryset = ResourceModel.objects.all()
    permission_classes = [IsAuthenticated, ResourcePermission]
    filter_backends = [DjangoFilterBackend, filters.SearchFilter, filters.OrderingFilter]
    filterset_fields = ["resource_type", "company", "is_active"]
    search_fields = ["name", "code", "email"]
    ordering_fields = ["name", "created_at"]

    def get_serializer_class(self):
        if self.action == "list":
            return ResourceListSerializer
        if self.action == "create":
            return ResourceCreateSerializer
        return ResourceDetailSerializer


# ═══════════════════════════════════════════════════
# Resource Assignment ViewSet
# ═══════════════════════════════════════════════════

class ResourceAssignmentViewSet(TenantFilterMixin, viewsets.ModelViewSet):
    """API تخصیص منبع به تسک."""
    queryset = ResourceAssignmentModel.objects.select_related("resource").all()
    permission_classes = [IsAuthenticated, ResourcePermission]
    filter_backends = [DjangoFilterBackend]
    filterset_fields = ["project", "task", "resource", "is_active"]

    def get_serializer_class(self):
        if self.action == "create":
            return AssignmentCreateSerializer
        return AssignmentSerializer


# ═══════════════════════════════════════════════════
# Budget ViewSet
# ═══════════════════════════════════════════════════

class BudgetViewSet(TenantFilterMixin, viewsets.ModelViewSet):
    """API مدیریت بودجه."""
    queryset = BudgetModel.objects.all()
    permission_classes = [IsAuthenticated, CostPermission]
    filter_backends = [DjangoFilterBackend]
    filterset_fields = ["project", "task", "is_active"]

    def get_serializer_class(self):
        if self.action == "create":
            return BudgetCreateSerializer
        return BudgetSerializer

    @action(detail=False, methods=["get"])
    def summary(self, request):
        """خلاصه بودجه پروژه."""
        project_id = request.query_params.get("project")
        if not project_id:
            return Response(
                {"error": {"message": "پارامتر project الزامی است."}},
                status=status.HTTP_400_BAD_REQUEST,
            )
        from django.db.models import Sum
        qs = self.get_queryset().filter(project_id=project_id, is_active=True)
        result = qs.aggregate(
            total_original=Sum("original_budget"),
            total_revised=Sum("revised_budget"),
            total_committed=Sum("committed_cost"),
            total_actual=Sum("actual_cost"),
        )
        return Response({"data": {k: v or 0 for k, v in result.items()}})


# ═══════════════════════════════════════════════════
# Cost Entry ViewSet
# ═══════════════════════════════════════════════════

class CostEntryViewSet(TenantFilterMixin, viewsets.ModelViewSet):
    """API ثبت هزینه."""
    queryset = CostEntryModel.objects.all()
    permission_classes = [IsAuthenticated, CostPermission]
    filter_backends = [DjangoFilterBackend, filters.OrderingFilter]
    filterset_fields = ["project", "task", "resource", "cost_type"]
    ordering_fields = ["entry_date", "amount"]
    ordering = ["-entry_date"]

    def get_serializer_class(self):
        if self.action == "create":
            return CostEntryCreateSerializer
        return CostEntrySerializer

    def perform_create(self, serializer):
        kwargs = {"created_by": self.request.user}
        if hasattr(self.request, "tenant"):
            kwargs["tenant"] = self.request.tenant
        serializer.save(**kwargs)


# ═══════════════════════════════════════════════════
# Risk ViewSet
# ═══════════════════════════════════════════════════

class RiskViewSet(TenantFilterMixin, viewsets.ModelViewSet):
    """API مدیریت ریسک."""
    queryset = RiskModel.objects.all()
    permission_classes = [IsAuthenticated, RiskPermission]
    filter_backends = [DjangoFilterBackend, filters.SearchFilter, filters.OrderingFilter]
    filterset_fields = ["project", "status", "probability", "impact", "owner"]
    search_fields = ["code", "title", "description"]
    ordering = ["-created_at"]

    def get_serializer_class(self):
        if self.action == "list":
            return RiskListSerializer
        if self.action == "create":
            return RiskCreateSerializer
        return RiskDetailSerializer

    @action(detail=False, methods=["get"])
    def matrix(self, request):
        """ماتریس ریسک."""
        project_id = request.query_params.get("project")
        if not project_id:
            return Response(
                {"error": {"message": "پارامتر project الزامی است."}},
                status=status.HTTP_400_BAD_REQUEST,
            )
        risks = self.get_queryset().filter(
            project_id=project_id
        ).exclude(status__in=["closed", "occurred"])

        matrix = {}
        for risk in risks:
            key = f"{risk.probability}_{risk.impact}"
            if key not in matrix:
                matrix[key] = []
            matrix[key].append({
                "id": str(risk.id), "title": risk.title,
                "score": risk.risk_score,
            })
        return Response({"data": matrix})


# ═══════════════════════════════════════════════════
# Baseline ViewSet
# ═══════════════════════════════════════════════════

class BaselineViewSet(TenantFilterMixin, viewsets.ModelViewSet):
    """API مدیریت خط پایه."""
    queryset = BaselineModel.objects.all()
    permission_classes = [IsAuthenticated, ProjectPermission]
    filter_backends = [DjangoFilterBackend]
    filterset_fields = ["project", "is_active", "baseline_type"]

    def get_serializer_class(self):
        if self.action == "create":
            return BaselineCreateSerializer
        return BaselineSerializer

    def perform_create(self, serializer):
        from ...application.services.supporting_services import BaselineService
        tenant = getattr(self.request, "tenant", None)
        tenant_id = tenant.id if tenant else None
        if tenant_id:
            from ...application.dtos.pm_dtos import CreateBaselineDTO
            dto = CreateBaselineDTO(
                project_id=serializer.validated_data["project"],
                name=serializer.validated_data["name"],
                description=serializer.validated_data.get("description", ""),
                baseline_type=serializer.validated_data.get("baseline_type", "initial"),
                tenant_id=tenant_id,
                created_by=self.request.user.id,
            )
            svc = BaselineService(tenant_id)
            svc.create_baseline(dto)


# ═══════════════════════════════════════════════════
# Calendar & Holiday ViewSets
# ═══════════════════════════════════════════════════

class CalendarViewSet(TenantFilterMixin, viewsets.ModelViewSet):
    """API مدیریت تقویم."""
    queryset = CalendarModel.objects.all()
    serializer_class = CalendarSerializer
    permission_classes = [IsAuthenticated, CalendarPermission]


class HolidayViewSet(TenantFilterMixin, viewsets.ModelViewSet):
    """API مدیریت تعطیلات."""
    queryset = HolidayModel.objects.all()
    serializer_class = HolidaySerializer
    permission_classes = [IsAuthenticated, CalendarPermission]
    filter_backends = [DjangoFilterBackend]
    filterset_fields = ["calendar"]


# ═══════════════════════════════════════════════════
# Change Request ViewSet
# ═══════════════════════════════════════════════════

class ChangeRequestViewSet(TenantFilterMixin, viewsets.ModelViewSet):
    """API درخواست تغییر."""
    queryset = ChangeRequestModel.objects.all()
    permission_classes = [IsAuthenticated, ProjectPermission]
    filter_backends = [DjangoFilterBackend, filters.SearchFilter]
    filterset_fields = ["project", "status", "priority"]
    search_fields = ["code", "title"]

    def get_serializer_class(self):
        if self.action == "list":
            return ChangeRequestListSerializer
        if self.action == "create":
            return ChangeRequestCreateSerializer
        return ChangeRequestDetailSerializer

    def perform_create(self, serializer):
        kwargs = {"requester": self.request.user}
        if hasattr(self.request, "tenant"):
            kwargs["tenant"] = self.request.tenant
        serializer.save(**kwargs)


# ═══════════════════════════════════════════════════
# Timesheet ViewSet
# ═══════════════════════════════════════════════════

class TimesheetViewSet(TenantFilterMixin, viewsets.ModelViewSet):
    """API تایم‌شیت."""
    queryset = TimesheetModel.objects.all()
    permission_classes = [IsAuthenticated, TaskPermission]
    filter_backends = [DjangoFilterBackend, filters.OrderingFilter]
    filterset_fields = ["project", "task", "user", "status"]
    ordering = ["-work_date"]

    def get_serializer_class(self):
        if self.action == "create":
            return TimesheetCreateSerializer
        return TimesheetSerializer

    def perform_create(self, serializer):
        kwargs = {"user": self.request.user}
        if hasattr(self.request, "tenant"):
            kwargs["tenant"] = self.request.tenant
        serializer.save(**kwargs)

    @action(detail=True, methods=["post"])
    def approve(self, request, pk=None):
        """تأیید تایم‌شیت."""
        ts = self.get_object()
        if ts.status != "submitted":
            return Response(
                {"error": {"message": "فقط تایم‌شیت‌های ارسال‌شده قابل تأیید هستند."}},
                status=status.HTTP_422_UNPROCESSABLE_ENTITY,
            )
        from datetime import date
        ts.status = "approved"
        ts.approved_by = request.user
        ts.approved_date = date.today()
        ts.save()
        return Response(TimesheetSerializer(ts).data)


# ═══════════════════════════════════════════════════
# Project Member ViewSet
# ═══════════════════════════════════════════════════

class ProjectMemberViewSet(TenantFilterMixin, viewsets.ModelViewSet):
    """API اعضای پروژه."""
    queryset = ProjectMemberModel.objects.all()
    permission_classes = [IsAuthenticated, ProjectPermission]
    filter_backends = [DjangoFilterBackend]
    filterset_fields = ["project", "user", "role", "is_active"]

    def get_serializer_class(self):
        if self.action == "create":
            return MemberCreateSerializer
        return MemberSerializer


# ═══════════════════════════════════════════════════
# Comment ViewSet
# ═══════════════════════════════════════════════════

class CommentViewSet(TenantFilterMixin, viewsets.ModelViewSet):
    """API نظرات."""
    queryset = CommentModel.objects.filter(is_deleted=False)
    permission_classes = [IsAuthenticated]
    filter_backends = [DjangoFilterBackend]
    filterset_fields = ["project", "task", "parent"]
    ordering = ["created_at"]

    def get_serializer_class(self):
        if self.action == "create":
            return CommentCreateSerializer
        return CommentSerializer

    def perform_create(self, serializer):
        kwargs = {"user": self.request.user}
        if hasattr(self.request, "tenant"):
            kwargs["tenant"] = self.request.tenant
        serializer.save(**kwargs)

    def perform_destroy(self, instance):
        """Soft delete — فقط علامت‌گذاری."""
        instance.is_deleted = True
        instance.save()


# ═══════════════════════════════════════════════════
# Activity Log ViewSet (Read-Only)
# ═══════════════════════════════════════════════════

class ActivityLogViewSet(TenantFilterMixin, viewsets.ReadOnlyModelViewSet):
    """API لاگ فعالیت — فقط خواندنی."""
    queryset = ActivityLogModel.objects.all()
    serializer_class = ActivityLogSerializer
    permission_classes = [IsAuthenticated, ProjectPermission]
    filter_backends = [DjangoFilterBackend, filters.OrderingFilter]
    filterset_fields = ["project", "task", "user", "action", "entity_type"]
    ordering = ["-created_at"]


# ═══════════════════════════════════════════════════
# Task Label ViewSets (Kanban)
# ═══════════════════════════════════════════════════

class TaskLabelViewSet(TenantFilterMixin, viewsets.ModelViewSet):
    """API مدیریت برچسب‌های تسک (Kanban)."""
    queryset = TaskLabelModel.objects.all()
    permission_classes = [IsAuthenticated, TaskPermission]
    filter_backends = [DjangoFilterBackend]
    filterset_fields = ["project"]

    def get_serializer_class(self):
        if self.action == "create":
            return TaskLabelCreateSerializer
        return TaskLabelSerializer


class TaskLabelAssignmentViewSet(TenantFilterMixin, viewsets.ModelViewSet):
    """API اختصاص برچسب به تسک."""
    queryset = TaskLabelAssignmentModel.objects.select_related("label").all()
    permission_classes = [IsAuthenticated, TaskPermission]
    filter_backends = [DjangoFilterBackend]
    filterset_fields = ["task", "label"]

    def get_serializer_class(self):
        if self.action == "create":
            return TaskLabelAssignmentCreateSerializer
        return TaskLabelAssignmentSerializer


# ═══════════════════════════════════════════════════
# Task Attachment ViewSet (Kanban)
# ═══════════════════════════════════════════════════

class TaskAttachmentViewSet(TenantFilterMixin, viewsets.ModelViewSet):
    """API پیوست فایل تسک."""
    queryset = TaskAttachmentModel.objects.all()
    permission_classes = [IsAuthenticated, TaskPermission]
    filter_backends = [DjangoFilterBackend]
    filterset_fields = ["task"]

    def get_serializer_class(self):
        if self.action == "create":
            return TaskAttachmentCreateSerializer
        return TaskAttachmentSerializer

    def perform_create(self, serializer):
        kwargs = {"uploaded_by": self.request.user}
        if hasattr(self.request, "tenant"):
            kwargs["tenant"] = self.request.tenant
        serializer.save(**kwargs)


# ═══════════════════════════════════════════════════
# Task Checklist ViewSets (Kanban)
# ═══════════════════════════════════════════════════

class TaskChecklistViewSet(TenantFilterMixin, viewsets.ModelViewSet):
    """API چک‌لیست تسک."""
    queryset = TaskChecklistModel.objects.prefetch_related("items").all()
    permission_classes = [IsAuthenticated, TaskPermission]
    filter_backends = [DjangoFilterBackend]
    filterset_fields = ["task"]

    def get_serializer_class(self):
        if self.action == "create":
            return TaskChecklistCreateSerializer
        return TaskChecklistSerializer


class TaskChecklistItemViewSet(TenantFilterMixin, viewsets.ModelViewSet):
    """API آیتم چک‌لیست."""
    queryset = TaskChecklistItemModel.objects.all()
    permission_classes = [IsAuthenticated, TaskPermission]
    filter_backends = [DjangoFilterBackend]
    filterset_fields = ["checklist", "is_checked", "assignee"]

    def get_serializer_class(self):
        if self.action == "create":
            return TaskChecklistItemCreateSerializer
        return TaskChecklistItemSerializer

    @action(detail=True, methods=["post"])
    def toggle(self, request, pk=None):
        """تغییر وضعیت تیک آیتم."""
        item = self.get_object()
        item.is_checked = not item.is_checked
        item.save()
        return Response(TaskChecklistItemSerializer(item).data)


# ═══════════════════════════════════════════════════
# RACI Assignment ViewSet
# ═══════════════════════════════════════════════════

class RACIAssignmentViewSet(TenantFilterMixin, viewsets.ModelViewSet):
    """API مدیریت نقش‌های RACI."""
    queryset = RACIAssignmentModel.objects.all()
    permission_classes = [IsAuthenticated, ProjectPermission]
    filter_backends = [DjangoFilterBackend, filters.OrderingFilter]
    filterset_fields = ["project", "task", "user", "role"]
    ordering = ["project", "task", "role"]

    def get_serializer_class(self):
        if self.action == "create":
            return RACIAssignmentCreateSerializer
        return RACIAssignmentSerializer

    @action(detail=False, methods=["get"])
    def matrix(self, request):
        """ماتریس RACI پروژه — جدول اعضا × تسک‌ها."""
        project_id = request.query_params.get("project")
        if not project_id:
            return Response(
                {"error": {"message": "پارامتر project الزامی است."}},
                status=status.HTTP_400_BAD_REQUEST,
            )

        assignments = self.get_queryset().filter(
            project_id=project_id,
        ).select_related("user")

        # Build matrix: {user_id: {task_id/null: role}}
        matrix = {}
        for a in assignments:
            uid = str(a.user_id)
            if uid not in matrix:
                matrix[uid] = {
                    "user_id": uid,
                    "user_name": str(a.user),
                    "assignments": {},
                }
            task_key = str(a.task_id) if a.task_id else "project"
            matrix[uid]["assignments"][task_key] = a.role

        # Get tasks for columns
        tasks = TaskModel.objects.filter(
            project_id=project_id
        ).values("id", "code", "title").order_by("sort_order")

        return Response({
            "data": {
                "tasks": list(tasks),
                "members": list(matrix.values()),
            }
        })

    @action(detail=False, methods=["post"], url_path="bulk-update")
    def bulk_update(self, request):
        """به‌روزرسانی دسته‌ای RACI."""
        project_id = request.data.get("project")
        assignments_data = request.data.get("assignments", [])

        if not project_id:
            return Response(
                {"error": {"message": "پارامتر project الزامی است."}},
                status=status.HTTP_400_BAD_REQUEST,
            )

        created = 0
        updated = 0
        for item in assignments_data:
            user_id = item.get("user")
            task_id = item.get("task")
            role = item.get("role")

            if not user_id or not role:
                continue

            kwargs = {
                "project_id": project_id,
                "task_id": task_id,
                "user_id": user_id,
                "role": role,
            }
            if hasattr(request, "tenant"):
                kwargs["tenant"] = request.tenant

            obj, is_new = RACIAssignmentModel.objects.update_or_create(
                project_id=project_id,
                task_id=task_id,
                user_id=user_id,
                role=role,
                defaults={"notes": item.get("notes", "")},
            )
            if is_new:
                if hasattr(request, "tenant"):
                    obj.tenant = request.tenant
                    obj.save()
                created += 1
            else:
                updated += 1

        return Response({
            "data": {"created": created, "updated": updated},
        })

    @action(detail=False, methods=["post"], url_path="multi-role")
    def multi_role(self, request):
        """ایجاد چند نقش RACI برای یک کاربر/منبع در یک تسک."""
        project_id = request.data.get("project")
        task_id = request.data.get("task") or None
        user_id = request.data.get("user")
        resource_id = request.data.get("resource")
        roles = request.data.get("roles", [])
        notes = request.data.get("notes", "")

        # Auto-resolve project from task when project is not provided
        if task_id and not project_id:
            try:
                task = TaskModel.objects.get(id=task_id)
                project_id = str(task.project_id)
            except TaskModel.DoesNotExist:
                return Response(
                    {"error": {"message": "فعالیت یافت نشد."}},
                    status=status.HTTP_400_BAD_REQUEST,
                )

        if not project_id:
            return Response(
                {"error": {"message": "پارامتر project الزامی است."}},
                status=status.HTTP_400_BAD_REQUEST,
            )
        if not roles:
            return Response(
                {"error": {"message": "حداقل یک نقش باید انتخاب شود."}},
                status=status.HTTP_400_BAD_REQUEST,
            )
        if not user_id and not resource_id:
            return Response(
                {"error": {"message": "کاربر یا منبع الزامی است."}},
                status=status.HTTP_400_BAD_REQUEST,
            )

        valid_roles = {"R", "A", "C", "I"}
        created = 0
        for role in roles:
            if role not in valid_roles:
                continue
            kwargs = {
                "project_id": project_id,
                "task_id": task_id,
                "user_id": user_id,
                "resource_id": resource_id,
                "role": role,
            }
            if hasattr(request, "tenant"):
                kwargs["tenant"] = request.tenant

            obj, is_new = RACIAssignmentModel.objects.get_or_create(
                project_id=project_id,
                task_id=task_id,
                user_id=user_id,
                resource_id=resource_id,
                role=role,
                defaults={"notes": notes},
            )
            if is_new:
                if hasattr(request, "tenant") and not obj.tenant:
                    obj.tenant = request.tenant
                    obj.save()
                created += 1

        return Response({
            "data": {"created": created},
        }, status=status.HTTP_201_CREATED)


# ═══════════════════════════════════════════════════
# Kanban Board ViewSet
# ═══════════════════════════════════════════════════

class KanbanBoardViewSet(TenantFilterMixin, viewsets.ViewSet):
    """API ویو کانبان برد — جابجایی کارت‌ها و دریافت ستون‌ها."""
    permission_classes = [IsAuthenticated, TaskPermission]

    def list(self, request):
        """دریافت داده‌های کانبان برد — تسک‌ها گروه‌بندی بر اساس وضعیت."""
        project_id = request.query_params.get("project")
        if not project_id:
            return Response(
                {"error": {"message": "پارامتر project الزامی است."}},
                status=status.HTTP_400_BAD_REQUEST,
            )

        qs = TaskModel.objects.filter(project_id=project_id)
        if hasattr(request, "tenant"):
            qs = qs.filter(tenant=request.tenant)

        # Exclude summary tasks from kanban
        qs = qs.exclude(task_type="summary").order_by("sort_order", "created_at")

        columns = [
            {"key": "not_started", "title": "شروع نشده", "color": "#6B7280"},
            {"key": "ready", "title": "آماده", "color": "#8B5CF6"},
            {"key": "in_progress", "title": "در حال انجام", "color": "#3B82F6"},
            {"key": "on_hold", "title": "متوقف", "color": "#F59E0B"},
            {"key": "completed", "title": "تکمیل‌شده", "color": "#10B981"},
            {"key": "cancelled", "title": "لغوشده", "color": "#EF4444"},
        ]

        cards_by_status = {}
        for col in columns:
            tasks_in_col = qs.filter(status=col["key"])
            cards_by_status[col["key"]] = KanbanCardSerializer(
                tasks_in_col, many=True
            ).data

        # Project labels
        labels = TaskLabelModel.objects.filter(project_id=project_id)
        if hasattr(request, "tenant"):
            labels = labels.filter(tenant=request.tenant)

        return Response({
            "data": {
                "columns": columns,
                "cards": cards_by_status,
                "labels": TaskLabelSerializer(labels, many=True).data,
            }
        })

    @action(detail=False, methods=["post"], url_path="move-card")
    def move_card(self, request):
        """جابجایی کارت بین ستون‌ها (drag & drop)."""
        task_id = request.data.get("task_id")
        new_status = request.data.get("status")
        new_sort_order = request.data.get("sort_order")

        if not task_id or not new_status:
            return Response(
                {"error": {"message": "task_id و status الزامی هستند."}},
                status=status.HTTP_400_BAD_REQUEST,
            )

        try:
            qs = TaskModel.objects.all()
            if hasattr(request, "tenant"):
                qs = qs.filter(tenant=request.tenant)
            task = qs.get(pk=task_id)
        except TaskModel.DoesNotExist:
            return Response(
                {"error": {"message": "تسک یافت نشد."}},
                status=status.HTTP_404_NOT_FOUND,
            )

        from datetime import date
        old_status = task.status
        task.status = new_status

        if new_status == "in_progress" and not task.actual_start:
            task.actual_start = date.today()
        elif new_status == "completed":
            task.actual_end = date.today()
            task.progress = 100

        if new_sort_order is not None:
            task.sort_order = new_sort_order

        task.save()

        return Response({
            "data": {
                "id": str(task.id),
                "old_status": old_status,
                "new_status": new_status,
                "task": KanbanCardSerializer(task).data,
            }
        })


# ═══════════════════════════════════════════════════
# Task Assignment ViewSet (Multi-assignment)
# ═══════════════════════════════════════════════════

class TaskAssignmentViewSet(TenantFilterMixin, viewsets.ModelViewSet):
    """مدیریت تخصیص‌های چندگانه تسک."""
    queryset = TaskAssignmentModel.objects.select_related("user").all()
    permission_classes = [IsAuthenticated, TaskPermission]
    filter_backends = [DjangoFilterBackend]
    filterset_fields = ["task", "user", "role", "is_active"]

    def get_serializer_class(self):
        if self.action in ("create",):
            return TaskAssignmentCreateSerializer
        return TaskAssignmentSerializer


# ═══════════════════════════════════════════════════
# Activity Code ViewSets
# ═══════════════════════════════════════════════════

class ActivityCodeTypeViewSet(TenantFilterMixin, viewsets.ModelViewSet):
    """مدیریت نوع کدهای فعالیت."""
    queryset = ActivityCodeTypeModel.objects.all()
    permission_classes = [IsAuthenticated]
    filter_backends = [DjangoFilterBackend, filters.SearchFilter]
    filterset_fields = ["scope", "project", "is_active"]
    search_fields = ["name", "code"]
    ordering = ["sort_order", "name"]

    def get_serializer_class(self):
        if self.action in ("create",):
            return ActivityCodeTypeCreateSerializer
        return ActivityCodeTypeSerializer


class ActivityCodeValueViewSet(TenantFilterMixin, viewsets.ModelViewSet):
    """مدیریت مقادیر کدهای فعالیت."""
    queryset = ActivityCodeValueModel.objects.all()
    permission_classes = [IsAuthenticated]
    filter_backends = [DjangoFilterBackend]
    filterset_fields = ["code_type", "parent"]
    ordering = ["sort_order", "name"]

    def get_serializer_class(self):
        if self.action in ("create",):
            return ActivityCodeValueCreateSerializer
        return ActivityCodeValueSerializer


class TaskActivityCodeViewSet(TenantFilterMixin, viewsets.ModelViewSet):
    """تخصیص کد فعالیت به تسک."""
    queryset = TaskActivityCodeModel.objects.select_related(
        "code_value__code_type"
    ).all()
    permission_classes = [IsAuthenticated]
    filter_backends = [DjangoFilterBackend]
    filterset_fields = ["task", "code_value"]

    def get_serializer_class(self):
        if self.action in ("create",):
            return TaskActivityCodeCreateSerializer
        return TaskActivityCodeSerializer


# ═══════════════════════════════════════════════════
# UDF ViewSets (User Defined Fields)
# ═══════════════════════════════════════════════════

class UDFDefinitionViewSet(TenantFilterMixin, viewsets.ModelViewSet):
    """مدیریت تعریف فیلدهای سفارشی."""
    queryset = UDFDefinitionModel.objects.all()
    permission_classes = [IsAuthenticated]
    filter_backends = [DjangoFilterBackend, filters.SearchFilter]
    filterset_fields = ["field_type", "entity_type", "is_active"]
    search_fields = ["name", "code"]
    ordering = ["sort_order", "name"]

    def get_serializer_class(self):
        if self.action in ("create",):
            return UDFDefinitionCreateSerializer
        return UDFDefinitionSerializer


class UDFValueViewSet(TenantFilterMixin, viewsets.ModelViewSet):
    """مدیریت مقادیر فیلدهای سفارشی."""
    queryset = UDFValueModel.objects.select_related("definition").all()
    permission_classes = [IsAuthenticated]
    filter_backends = [DjangoFilterBackend]
    filterset_fields = ["definition", "entity_id", "entity_type"]

    def get_serializer_class(self):
        if self.action in ("create",):
            return UDFValueCreateSerializer
        return UDFValueSerializer


# ═══════════════════════════════════════════════════
# Resource Skill ViewSet
# ═══════════════════════════════════════════════════

class ResourceSkillViewSet(TenantFilterMixin, viewsets.ModelViewSet):
    """مدیریت مهارت‌های منابع."""
    queryset = ResourceSkillModel.objects.all()
    permission_classes = [IsAuthenticated, ResourcePermission]
    filter_backends = [DjangoFilterBackend, filters.SearchFilter]
    filterset_fields = ["resource"]
    search_fields = ["skill_name"]

    def get_serializer_class(self):
        if self.action in ("create",):
            return ResourceSkillCreateSerializer
        return ResourceSkillSerializer


# ═══════════════════════════════════════════════════
# Resource Availability ViewSet
# ═══════════════════════════════════════════════════

class ResourceAvailabilityViewSet(TenantFilterMixin, viewsets.ModelViewSet):
    """مدیریت دسترسی منابع."""
    queryset = ResourceAvailabilityModel.objects.all()
    permission_classes = [IsAuthenticated, ResourcePermission]
    filter_backends = [DjangoFilterBackend]
    filterset_fields = ["resource"]
    ordering = ["-start_date"]

    def get_serializer_class(self):
        if self.action in ("create",):
            return ResourceAvailabilityCreateSerializer
        return ResourceAvailabilitySerializer


# ═══════════════════════════════════════════════════
# Resource Cost Rate ViewSet
# ═══════════════════════════════════════════════════

class ResourceCostRateViewSet(TenantFilterMixin, viewsets.ModelViewSet):
    """مدیریت نرخ‌های هزینه منابع."""
    queryset = ResourceCostRateModel.objects.all()
    permission_classes = [IsAuthenticated, ResourcePermission]
    filter_backends = [DjangoFilterBackend]
    filterset_fields = ["resource"]
    ordering = ["-effective_date"]

    def get_serializer_class(self):
        if self.action in ("create",):
            return ResourceCostRateCreateSerializer
        return ResourceCostRateSerializer


# ═══════════════════════════════════════════════════
# Cost Account ViewSet (CBS)
# ═══════════════════════════════════════════════════

class CostAccountViewSet(TenantFilterMixin, viewsets.ModelViewSet):
    """مدیریت حساب‌های هزینه (CBS)."""
    queryset = CostAccountModel.objects.all()
    permission_classes = [IsAuthenticated, CostPermission]
    filter_backends = [DjangoFilterBackend, filters.SearchFilter]
    filterset_fields = ["project", "parent"]
    search_fields = ["code", "name"]
    ordering = ["sort_order", "code"]

    def get_serializer_class(self):
        if self.action in ("create",):
            return CostAccountCreateSerializer
        return CostAccountSerializer

    @action(detail=False, methods=["get"], url_path="tree")
    def tree(self, request):
        """ساختار درختی CBS."""
        project_id = request.query_params.get("project")
        if not project_id:
            return Response(
                {"error": {"message": "پارامتر project الزامی است."}},
                status=status.HTTP_400_BAD_REQUEST,
            )
        qs = self.get_queryset().filter(project_id=project_id, parent__isnull=True)
        return Response({"data": CostAccountSerializer(qs, many=True).data})


# ═══════════════════════════════════════════════════
# Funding Source ViewSet
# ═══════════════════════════════════════════════════

class FundingSourceViewSet(TenantFilterMixin, viewsets.ModelViewSet):
    """مدیریت منابع مالی."""
    queryset = FundingSourceModel.objects.all()
    permission_classes = [IsAuthenticated, CostPermission]
    filter_backends = [DjangoFilterBackend]
    filterset_fields = ["project", "is_active"]

    def get_serializer_class(self):
        if self.action in ("create",):
            return FundingSourceCreateSerializer
        return FundingSourceSerializer


# ═══════════════════════════════════════════════════
# Cash Flow ViewSet
# ═══════════════════════════════════════════════════

class CashFlowEntryViewSet(TenantFilterMixin, viewsets.ModelViewSet):
    """مدیریت جریان نقدی."""
    queryset = CashFlowEntryModel.objects.all()
    permission_classes = [IsAuthenticated, CostPermission]
    filter_backends = [DjangoFilterBackend]
    filterset_fields = ["project"]
    ordering = ["period_start"]

    def get_serializer_class(self):
        if self.action in ("create",):
            return CashFlowEntryCreateSerializer
        return CashFlowEntrySerializer


# ═══════════════════════════════════════════════════
# Schedule Health ViewSet
# ═══════════════════════════════════════════════════

class ScheduleHealthViewSet(TenantFilterMixin, viewsets.ReadOnlyModelViewSet):
    """ارزیابی و مشاهده سلامت زمان‌بندی."""
    queryset = ScheduleHealthSnapshotModel.objects.all()
    serializer_class = ScheduleHealthSnapshotSerializer
    permission_classes = [IsAuthenticated]
    filter_backends = [DjangoFilterBackend]
    filterset_fields = ["project"]
    ordering = ["-snapshot_date"]

    @action(detail=False, methods=["post"], url_path="run")
    def run_assessment(self, request):
        """اجرای ارزیابی سلامت زمان‌بندی."""
        ser = ScheduleHealthRunSerializer(data=request.data)
        ser.is_valid(raise_exception=True)

        from ...application.services.advanced_services import ScheduleHealthAppService
        tenant_id = request.tenant.id if hasattr(request, "tenant") else None
        svc = ScheduleHealthAppService(tenant_id)
        snapshot = svc.assess_and_save(
            project_id=ser.validated_data["project_id"],
            baseline_end=ser.validated_data.get("baseline_end"),
        )
        return Response(
            {"data": ScheduleHealthSnapshotSerializer(snapshot).data}
            if hasattr(snapshot, "id") else {"data": str(snapshot)},
            status=status.HTTP_201_CREATED,
        )

    @action(detail=False, methods=["get"], url_path="latest")
    def latest(self, request):
        """آخرین اسنپ‌شات سلامت."""
        project_id = request.query_params.get("project")
        if not project_id:
            return Response(
                {"error": {"message": "پارامتر project الزامی است."}},
                status=status.HTTP_400_BAD_REQUEST,
            )
        qs = self.get_queryset().filter(project_id=project_id).order_by("-snapshot_date")
        snapshot = qs.first()
        if not snapshot:
            return Response(
                {"error": {"message": "اسنپ‌شاتی یافت نشد."}},
                status=status.HTTP_404_NOT_FOUND,
            )
        return Response({"data": ScheduleHealthSnapshotSerializer(snapshot).data})


# ═══════════════════════════════════════════════════
# Project Template ViewSet
# ═══════════════════════════════════════════════════

class ProjectTemplateViewSet(TenantFilterMixin, viewsets.ModelViewSet):
    """مدیریت قالب‌های پروژه."""
    queryset = ProjectTemplateModel.objects.all()
    permission_classes = [IsAuthenticated]
    filter_backends = [DjangoFilterBackend, filters.SearchFilter]
    filterset_fields = ["category", "is_active"]
    search_fields = ["name", "description"]
    ordering = ["-created_at"]

    def get_serializer_class(self):
        if self.action == "list":
            return ProjectTemplateListSerializer
        if self.action in ("create",):
            return ProjectTemplateCreateSerializer
        if self.action == "save_from_project":
            return SaveProjectAsTemplateSerializer
        return ProjectTemplateDetailSerializer

    @action(detail=False, methods=["post"], url_path="save-from-project")
    def save_from_project(self, request):
        """ذخیره پروژه موجود به عنوان قالب."""
        ser = SaveProjectAsTemplateSerializer(data=request.data)
        ser.is_valid(raise_exception=True)

        from ...application.services.advanced_services import ProjectTemplateService
        tenant_id = request.tenant.id if hasattr(request, "tenant") else None
        svc = ProjectTemplateService(tenant_id)
        template = svc.save_from_project(
            project_id=ser.validated_data["project_id"],
            name=ser.validated_data["name"],
            description=ser.validated_data.get("description", ""),
        )
        return Response(
            {"data": ProjectTemplateDetailSerializer(template).data}
            if hasattr(template, "id") else {"data": str(template)},
            status=status.HTTP_201_CREATED,
        )


# ═══════════════════════════════════════════════════
# Monte Carlo Simulation ViewSet
# ═══════════════════════════════════════════════════

class MonteCarloViewSet(viewsets.ViewSet):
    """شبیه‌سازی مونت‌کارلو."""
    permission_classes = [IsAuthenticated]

    def create(self, request):
        """اجرای شبیه‌سازی مونت‌کارلو."""
        ser = MonteCarloRunSerializer(data=request.data)
        ser.is_valid(raise_exception=True)

        from ...application.services.advanced_services import MonteCarloAppService
        tenant_id = request.tenant.id if hasattr(request, "tenant") else None
        svc = MonteCarloAppService(tenant_id)

        result = svc.run_simulation(
            project_id=ser.validated_data["project_id"],
            estimates=[
                {
                    "task_id": e["task_id"],
                    "optimistic": e["optimistic"],
                    "most_likely": e["most_likely"],
                    "pessimistic": e["pessimistic"],
                }
                for e in ser.validated_data["estimates"]
            ],
            iterations=ser.validated_data.get("iterations", 1000),
        )
        return Response({
            "data": MonteCarloResultSerializer({
                "mean": result.mean,
                "std_dev": result.std_dev,
                "p50": result.p50,
                "p80": result.p80,
                "p90": result.p90,
                "p95": result.p95,
                "histogram": result.histogram,
                "confidence_range": result.confidence_range,
            }).data,
        })


# ═══════════════════════════════════════════════════
# Resource Leveling ViewSet
# ═══════════════════════════════════════════════════

class ResourceLevelingViewSet(viewsets.ViewSet):
    """تسطیح منابع."""
    permission_classes = [IsAuthenticated]

    def create(self, request):
        """اجرای تسطیح منابع."""
        ser = ResourceLevelingRunSerializer(data=request.data)
        ser.is_valid(raise_exception=True)

        from ...application.services.advanced_services import ResourceLevelingAppService
        tenant_id = request.tenant.id if hasattr(request, "tenant") else None
        svc = ResourceLevelingAppService(tenant_id)

        result = svc.run_leveling(
            project_id=ser.validated_data["project_id"],
        )

        tasks_updated = 0
        if ser.validated_data.get("apply_results") and result.success:
            tasks_updated = svc.apply_leveling(
                project_id=ser.validated_data["project_id"],
                result=result,
            )

        # Convert UUID keys to strings for JSON serialization
        leveled_tasks = {str(k): str(v) for k, v in result.leveled_tasks.items()}
        overallocated = [str(r) for r in result.overallocated_resources]

        return Response({
            "data": ResourceLevelingResultSerializer({
                "leveled_tasks": leveled_tasks,
                "overallocated_resources": overallocated,
                "iterations": result.iterations,
                "success": result.success,
                "tasks_updated": tasks_updated,
            }).data,
        })


# ═══════════════════════════════════════════════════
# Issue ViewSet
# ═══════════════════════════════════════════════════

class IssueViewSet(TenantFilterMixin, viewsets.ModelViewSet):
    """API مدیریت مسائل پروژه (Issue Log)."""
    queryset = IssueModel.objects.all()
    permission_classes = [IsAuthenticated, IssuePermission]
    filter_backends = [DjangoFilterBackend, filters.SearchFilter, filters.OrderingFilter]
    filterset_fields = ["project", "status", "severity", "priority", "category", "assignee", "reporter"]
    search_fields = ["code", "title", "description"]
    ordering = ["-created_at"]

    def get_serializer_class(self):
        if self.action == "list":
            return IssueListSerializer
        if self.action == "create":
            return IssueCreateSerializer
        return IssueDetailSerializer

    @action(detail=True, methods=["post"])
    def assign(self, request, pk=None):
        """تخصیص مسئله به کاربر."""
        ser = IssueAssignSerializer(data=request.data)
        ser.is_valid(raise_exception=True)
        issue = self.get_object()
        issue.assignee_id = ser.validated_data["assignee_id"]
        if issue.status == "open":
            issue.status = "in_progress"
        issue.save()
        return Response({"data": IssueDetailSerializer(issue).data})

    @action(detail=True, methods=["post"])
    def resolve(self, request, pk=None):
        """حل مسئله."""
        ser = IssueResolveSerializer(data=request.data)
        ser.is_valid(raise_exception=True)
        issue = self.get_object()
        if issue.status == "closed":
            return Response(
                {"error": {"message": "مسئله بسته‌شده قابل حل مجدد نیست."}},
                status=status.HTTP_400_BAD_REQUEST,
            )
        issue.resolution = ser.validated_data["resolution"]
        issue.status = "resolved"
        from datetime import date as _date
        issue.resolved_date = _date.today()
        issue.save()
        return Response({"data": IssueDetailSerializer(issue).data})

    @action(detail=True, methods=["post"])
    def close(self, request, pk=None):
        """بستن مسئله — فقط از وضعیت حل‌شده."""
        issue = self.get_object()
        if issue.status != "resolved":
            return Response(
                {"error": {"message": "فقط مسائل حل‌شده قابل بستن هستند."}},
                status=status.HTTP_400_BAD_REQUEST,
            )
        issue.status = "closed"
        from datetime import date as _date
        issue.closed_date = _date.today()
        issue.save()
        return Response({"data": IssueDetailSerializer(issue).data})

    @action(detail=True, methods=["post"])
    def reopen(self, request, pk=None):
        """بازگشایی مسئله."""
        issue = self.get_object()
        if issue.status not in ("resolved", "closed"):
            return Response(
                {"error": {"message": "فقط مسائل حل‌شده یا بسته‌شده قابل بازگشایی هستند."}},
                status=status.HTTP_400_BAD_REQUEST,
            )
        issue.status = "open"
        issue.resolved_date = None
        issue.closed_date = None
        issue.resolution = ""
        issue.save()
        return Response({"data": IssueDetailSerializer(issue).data})

    @action(detail=False, methods=["get"])
    def summary(self, request):
        """خلاصه وضعیت مسائل یک پروژه."""
        project_id = request.query_params.get("project")
        if not project_id:
            return Response(
                {"error": {"message": "پارامتر project الزامی است."}},
                status=status.HTTP_400_BAD_REQUEST,
            )
        from django.db.models import Count
        qs = self.get_queryset().filter(project_id=project_id)
        by_status = dict(qs.values_list("status").annotate(count=Count("id")).values_list("status", "count"))
        by_severity = dict(qs.values_list("severity").annotate(count=Count("id")).values_list("severity", "count"))
        return Response({
            "data": {
                "total": qs.count(),
                "by_status": by_status,
                "by_severity": by_severity,
            }
        })


# ═══════════════════════════════════════════════════
# OBS ViewSet
# ═══════════════════════════════════════════════════

class OBSNodeViewSet(TenantFilterMixin, viewsets.ModelViewSet):
    """CRUD + عملیات درختی ساختار شکست سازمانی (OBS)."""

    queryset = OBSNodeModel.objects.all()
    permission_classes = [IsAuthenticated, OBSPermission]
    filter_backends = [DjangoFilterBackend, filters.SearchFilter]
    filterset_fields = ["project", "parent", "node_type", "manager", "is_active"]
    search_fields = ["code", "name", "responsibilities"]
    ordering = ["sort_order", "code"]

    def get_serializer_class(self):
        if self.action in ("create", "update", "partial_update"):
            return OBSNodeCreateSerializer
        if self.action == "retrieve":
            return OBSNodeDetailSerializer
        return OBSNodeListSerializer

    def perform_create(self, serializer):
        # محاسبه سطح بر اساس والد
        parent = serializer.validated_data.get("parent")
        level = 0
        if parent:
            level = parent.level + 1
        serializer.save(
            tenant=self.request.tenant,
            level=level,
        )

    def perform_update(self, serializer):
        parent = serializer.validated_data.get("parent")
        extra = {}
        if parent is not None:
            extra["level"] = parent.level + 1 if parent else 0
        serializer.save(**extra)

    @action(detail=False, methods=["get"], url_path="tree")
    def tree(self, request):
        """ساختار درختی OBS پروژه."""
        project_id = request.query_params.get("project")
        if not project_id:
            return Response(
                {"error": {"message": "پارامتر project الزامی است."}},
                status=status.HTTP_400_BAD_REQUEST,
            )
        qs = self.get_queryset().filter(
            project_id=project_id, parent__isnull=True, is_active=True
        ).order_by("sort_order", "code")
        serializer = OBSNodeTreeSerializer(qs, many=True)
        return Response({"data": serializer.data})

    @action(detail=True, methods=["get"])
    def children(self, request, pk=None):
        """فرزندان مستقیم یک گره."""
        node = self.get_object()
        qs = OBSNodeModel.objects.filter(
            parent=node, tenant=request.tenant
        ).order_by("sort_order", "code")
        serializer = OBSNodeListSerializer(qs, many=True)
        return Response({"data": serializer.data})

    @action(detail=True, methods=["post"])
    def move(self, request, pk=None):
        """جابجایی گره به والد جدید."""
        node = self.get_object()
        new_parent_id = request.data.get("parent_id")
        new_sort_order = request.data.get("sort_order", node.sort_order)

        if new_parent_id:
            try:
                new_parent = OBSNodeModel.objects.get(
                    id=new_parent_id, tenant=request.tenant
                )
                node.parent = new_parent
                node.level = new_parent.level + 1
            except OBSNodeModel.DoesNotExist:
                return Response(
                    {"error": {"message": "والد مشخص‌شده یافت نشد."}},
                    status=status.HTTP_400_BAD_REQUEST,
                )
        else:
            node.parent = None
            node.level = 0

        node.sort_order = new_sort_order
        node.save()
        return Response({"data": OBSNodeDetailSerializer(node).data})


# ═══════════════════════════════════════════════════
# RBS ViewSet
# ═══════════════════════════════════════════════════

class RBSNodeViewSet(TenantFilterMixin, viewsets.ModelViewSet):
    """CRUD + عملیات درختی ساختار شکست منابع (RBS)."""

    queryset = RBSNodeModel.objects.all()
    permission_classes = [IsAuthenticated, RBSPermission]
    filter_backends = [DjangoFilterBackend, filters.SearchFilter]
    filterset_fields = ["project", "parent", "node_type", "resource_type", "is_active"]
    search_fields = ["code", "name", "resource_type", "unit"]
    ordering = ["sort_order", "code"]

    def get_serializer_class(self):
        if self.action in ("create", "update", "partial_update"):
            return RBSNodeCreateSerializer
        if self.action == "retrieve":
            return RBSNodeDetailSerializer
        return RBSNodeListSerializer

    def perform_create(self, serializer):
        parent = serializer.validated_data.get("parent")
        level = 0
        if parent:
            level = parent.level + 1
        serializer.save(
            tenant=self.request.tenant,
            level=level,
        )

    def perform_update(self, serializer):
        parent = serializer.validated_data.get("parent")
        extra = {}
        if parent is not None:
            extra["level"] = parent.level + 1 if parent else 0
        serializer.save(**extra)

    @action(detail=False, methods=["get"], url_path="tree")
    def tree(self, request):
        """ساختار درختی RBS پروژه."""
        project_id = request.query_params.get("project")
        if not project_id:
            return Response(
                {"error": {"message": "پارامتر project الزامی است."}},
                status=status.HTTP_400_BAD_REQUEST,
            )
        qs = self.get_queryset().filter(
            project_id=project_id, parent__isnull=True, is_active=True
        ).order_by("sort_order", "code")
        serializer = RBSNodeTreeSerializer(qs, many=True)
        return Response({"data": serializer.data})

    @action(detail=True, methods=["get"])
    def children(self, request, pk=None):
        """فرزندان مستقیم یک گره."""
        node = self.get_object()
        qs = RBSNodeModel.objects.filter(
            parent=node, tenant=request.tenant
        ).order_by("sort_order", "code")
        serializer = RBSNodeListSerializer(qs, many=True)
        return Response({"data": serializer.data})

    @action(detail=True, methods=["post"])
    def move(self, request, pk=None):
        """جابجایی گره به والد جدید."""
        node = self.get_object()
        new_parent_id = request.data.get("parent_id")
        new_sort_order = request.data.get("sort_order", node.sort_order)

        if new_parent_id:
            try:
                new_parent = RBSNodeModel.objects.get(
                    id=new_parent_id, tenant=request.tenant
                )
                node.parent = new_parent
                node.level = new_parent.level + 1
            except RBSNodeModel.DoesNotExist:
                return Response(
                    {"error": {"message": "والد مشخص‌شده یافت نشد."}},
                    status=status.HTTP_400_BAD_REQUEST,
                )
        else:
            node.parent = None
            node.level = 0

        node.sort_order = new_sort_order
        node.save()
        return Response({"data": RBSNodeDetailSerializer(node).data})


# ═══════════════════════════════════════════════════
# Quality Plan ViewSet
# ═══════════════════════════════════════════════════

class QualityPlanViewSet(TenantFilterMixin, viewsets.ModelViewSet):
    """CRUD طرح کیفیت."""

    queryset = QualityPlanModel.objects.all()
    permission_classes = [IsAuthenticated, QualityPlanPermission]
    filter_backends = [DjangoFilterBackend, filters.SearchFilter]
    filterset_fields = ["project", "status", "quality_manager"]
    search_fields = ["name", "criteria", "standards"]
    ordering = ["-created_at"]

    def get_serializer_class(self):
        if self.action in ("create", "update", "partial_update"):
            return QualityPlanCreateSerializer
        if self.action == "retrieve":
            return QualityPlanDetailSerializer
        return QualityPlanListSerializer

    def perform_create(self, serializer):
        serializer.save(tenant=self.request.tenant)


# ═══════════════════════════════════════════════════
# Inspection ViewSet
# ═══════════════════════════════════════════════════

class InspectionViewSet(TenantFilterMixin, viewsets.ModelViewSet):
    """CRUD بازرسی."""

    queryset = InspectionModel.objects.all()
    permission_classes = [IsAuthenticated, InspectionPermission]
    filter_backends = [DjangoFilterBackend, filters.SearchFilter]
    filterset_fields = ["project", "inspection_type", "status", "result", "inspector", "quality_plan"]
    search_fields = ["code", "title", "findings"]
    ordering = ["-planned_date"]

    def get_serializer_class(self):
        if self.action in ("create", "update", "partial_update"):
            return InspectionCreateSerializer
        if self.action == "retrieve":
            return InspectionDetailSerializer
        return InspectionListSerializer

    def perform_create(self, serializer):
        serializer.save(tenant=self.request.tenant)


# ═══════════════════════════════════════════════════
# NCR ViewSet
# ═══════════════════════════════════════════════════

class NCRViewSet(TenantFilterMixin, viewsets.ModelViewSet):
    """CRUD عدم انطباق (NCR)."""

    queryset = NCRModel.objects.all()
    permission_classes = [IsAuthenticated, NCRPermission]
    filter_backends = [DjangoFilterBackend, filters.SearchFilter]
    filterset_fields = ["project", "severity", "status", "inspection", "reported_by", "assigned_to"]
    search_fields = ["code", "title", "root_cause", "corrective_action"]
    ordering = ["-created_at"]

    def get_serializer_class(self):
        if self.action in ("create", "update", "partial_update"):
            return NCRCreateSerializer
        if self.action == "retrieve":
            return NCRDetailSerializer
        return NCRListSerializer

    def perform_create(self, serializer):
        serializer.save(tenant=self.request.tenant)


# ═══════════════════════════════════════════════════
# Quality Checklist ViewSet
# ═══════════════════════════════════════════════════

class QualityChecklistViewSet(TenantFilterMixin, viewsets.ModelViewSet):
    """CRUD چک‌لیست کیفیت."""

    queryset = QualityChecklistModel.objects.all()
    permission_classes = [IsAuthenticated, QualityChecklistPermission]
    filter_backends = [DjangoFilterBackend, filters.SearchFilter]
    filterset_fields = ["project", "quality_plan", "task", "is_completed"]
    search_fields = ["name", "description"]
    ordering = ["-created_at"]

    def get_serializer_class(self):
        if self.action in ("create", "update", "partial_update"):
            return QualityChecklistCreateSerializer
        if self.action == "retrieve":
            return QualityChecklistDetailSerializer
        return QualityChecklistListSerializer

    def perform_create(self, serializer):
        serializer.save(tenant=self.request.tenant)

    @action(detail=True, methods=["get"])
    def items(self, request, pk=None):
        """لیست آیتم‌های چک‌لیست."""
        checklist = self.get_object()
        items = QualityChecklistItemModel.objects.filter(
            checklist=checklist
        ).order_by("sort_order")
        serializer = QualityChecklistItemSerializer(items, many=True)
        return Response({"data": serializer.data})

    @action(detail=True, methods=["post"], url_path="items/create")
    def create_item(self, request, pk=None):
        """ایجاد آیتم در چک‌لیست."""
        checklist = self.get_object()
        data = request.data.copy()
        data["checklist"] = checklist.id
        serializer = QualityChecklistItemCreateSerializer(data=data)
        serializer.is_valid(raise_exception=True)
        serializer.save(tenant=request.tenant)
        return Response(
            {"data": serializer.data},
            status=status.HTTP_201_CREATED,
        )


class QualityChecklistItemViewSet(TenantFilterMixin, viewsets.ModelViewSet):
    """CRUD آیتم چک‌لیست کیفیت."""

    queryset = QualityChecklistItemModel.objects.all()
    permission_classes = [IsAuthenticated, QualityChecklistPermission]
    filter_backends = [DjangoFilterBackend]
    filterset_fields = ["checklist", "is_checked"]
    ordering = ["sort_order"]

    def get_serializer_class(self):
        if self.action == "create":
            return QualityChecklistItemCreateSerializer
        if self.action in ("update", "partial_update"):
            return QualityChecklistItemUpdateSerializer
        return QualityChecklistItemSerializer

    def perform_create(self, serializer):
        serializer.save(tenant=self.request.tenant)


# ═══════════════════════════════════════════════════
# Project Document ViewSet
# ═══════════════════════════════════════════════════

class ProjectDocumentViewSet(TenantFilterMixin, viewsets.ModelViewSet):
    """CRUD مستندات پروژه (لینک به سرویس کنترل مدارک)."""

    queryset = ProjectDocumentModel.objects.select_related(
        "project", "linked_by",
    ).all()
    permission_classes = [IsAuthenticated, ProjectDocumentPermission]
    filter_backends = [DjangoFilterBackend, filters.SearchFilter, filters.OrderingFilter]
    filterset_fields = ["project", "entity_type", "entity_id", "document_id"]
    search_fields = ["title", "document_code", "notes"]
    ordering_fields = ["created_at", "title", "document_code"]
    ordering = ["-created_at"]

    def get_serializer_class(self):
        if self.action == "create":
            return ProjectDocumentCreateSerializer
        return ProjectDocumentListSerializer

    def perform_create(self, serializer):
        serializer.save(
            tenant=self.request.tenant,
            linked_by=self.request.user,
        )

    @action(detail=False, methods=["get"], url_path="by-project/(?P<project_id>[^/.]+)")
    def by_project(self, request, project_id=None):
        """لیست مستندات یک پروژه خاص."""
        qs = self.get_queryset().filter(project_id=project_id)
        page = self.paginate_queryset(qs)
        if page is not None:
            serializer = ProjectDocumentListSerializer(page, many=True)
            return self.get_paginated_response(serializer.data)
        serializer = ProjectDocumentListSerializer(qs, many=True)
        return Response(serializer.data)

    @action(detail=False, methods=["get"], url_path="by-entity/(?P<entity_type>[^/.]+)/(?P<entity_id>[^/.]+)")
    def by_entity(self, request, entity_type=None, entity_id=None):
        """لیست مستندات یک موجودیت خاص (task, risk, ...)."""
        qs = self.get_queryset().filter(entity_type=entity_type, entity_id=entity_id)
        serializer = ProjectDocumentListSerializer(qs, many=True)
        return Response(serializer.data)
