"""
PM Module — Scenario 1 API Integration Tests.

تست‌های یکپارچه سناریوی ۱:
    ایجاد پروژه → تسک‌ها → RACI → وابستگی → پیشرفت → کارتابل → حذف
"""
import uuid
from datetime import date, timedelta
from decimal import Decimal

import pytest
from rest_framework.test import APIClient

from apps.core.tenant.models import Tenant, Domain
from apps.core.tenant.middleware import _thread_locals
from apps.core.auth.models import User

from modules.pm.backend.infrastructure.persistence.models import (
    ProjectModel,
    TaskModel,
    DependencyModel,
    RACIAssignmentModel,
    CommentModel,
    ActivityLogModel,
)

pytestmark = pytest.mark.django_db


# ═══════════════════════════════════════════════════════════
# Fixtures
# ═══════════════════════════════════════════════════════════

@pytest.fixture(autouse=True)
def _patch_tenant_middleware(tenant, monkeypatch):
    from apps.core.tenant import middleware as tenant_mw
    from django.db import connection
    monkeypatch.setattr(tenant_mw, "get_current_tenant", lambda: tenant)
    if hasattr(connection, "set_tenant"):
        connection.set_tenant(tenant)
    yield
    if hasattr(_thread_locals, "tenant"):
        del _thread_locals.tenant
    if hasattr(_thread_locals, "request"):
        del _thread_locals.request


@pytest.fixture(scope="function")
def tenant(db):
    t = Tenant.objects.create(
        name="Scenario Test Tenant",
        slug="scenario-test",
        schema_name="public",
    )
    Domain.objects.create(domain="localhost", tenant=t, is_primary=True)
    return t


@pytest.fixture
def admin_user(tenant):
    """ادمین — admin@nexa.app"""
    return User.objects.create_superuser(
        email="admin-scenario@nexa.app",
        password="adminpass123",
        first_name="ادمین",
        last_name="نکسا",
        tenant=tenant,
    )


@pytest.fixture
def employee_user(tenant):
    """کارمند — hossein.taheri@nexa.app"""
    return User.objects.create_user(
        email="hossein-scenario@nexa.app",
        password="employeepass123",
        first_name="حسین",
        last_name="طاهری",
        tenant=tenant,
    )


@pytest.fixture
def admin_client(admin_user):
    client = APIClient()
    client.force_authenticate(user=admin_user)
    return client


@pytest.fixture
def employee_client(employee_user):
    client = APIClient()
    client.force_authenticate(user=employee_user)
    return client


@pytest.fixture
def unauthenticated_client():
    return APIClient()


# ═══════════════════════════════════════════════════════════
# 4.1: ثبت پروژه جدید توسط ادمین
# ═══════════════════════════════════════════════════════════

class TestProjectCreation:
    """سناریوی ۴.۱ — ثبت پروژه."""

    URL = "/api/v1/pm/projects/"

    def test_admin_creates_project(self, admin_client):
        data = {
            "code": "PRJ-SAMPLE",
            "title": "پروژه نمونه",
            "description": "تست سناریوی RACI",
            "project_type": "project",
            "priority": "medium",
            "planned_start": "2025-03-21",  # ۱۴۰۴/۰۱/۰۱
            "planned_end": "2025-06-21",    # ۱۴۰۴/۰۳/۳۱
        }
        resp = admin_client.post(self.URL, data, format="json")
        assert resp.status_code in (200, 201), resp.data
        project = resp.data
        assert project["title"] == "پروژه نمونه"
        # progress is not in ProjectCreateSerializer; verify via GET
        detail_resp = admin_client.get(f"{self.URL}{project['id']}/")
        assert detail_resp.status_code == 200
        assert detail_resp.data["progress"] == 0

    def test_project_in_list(self, admin_client, tenant):
        ProjectModel.objects.create(
            id=uuid.uuid4(), tenant=tenant,
            code="PRJ-LIST", title="پروژه لیست",
            project_type="project", status="planning",
        )
        resp = admin_client.get(self.URL)
        assert resp.status_code == 200
        # Should have at least one project
        results = resp.data.get("results", resp.data)
        assert len(results) >= 1


# ═══════════════════════════════════════════════════════════
# 4.2 + 4.3 + 4.4: تسک‌ها، RACI، وابستگی
# ═══════════════════════════════════════════════════════════

class TestTaskCreationAndRaci:
    """سناریوی ۴.۲ تا ۴.۴ — ایجاد تسک، RACI، وابستگی."""

    TASK_URL = "/api/v1/pm/tasks/"
    RACI_URL = "/api/v1/pm/raci/"
    DEP_URL = "/api/v1/pm/dependencies/"

    @pytest.fixture
    def project(self, tenant, admin_user):
        return ProjectModel.objects.create(
            id=uuid.uuid4(), tenant=tenant,
            code="PRJ-RACI", title="پروژه نمونه",
            project_type="project", status="active",
            priority="medium", manager=admin_user,
            planned_start=date.today(),
            planned_end=date.today() + timedelta(days=90),
            actual_start=date.today(),
        )

    @pytest.fixture
    def four_tasks(self, tenant, project):
        """ایجاد ۴ تسک سناریو."""
        tasks = []
        titles = ["تحلیل نیازمندی‌ها", "طراحی معماری", "مستندسازی", "بررسی فنی"]
        for i, title in enumerate(titles, 1):
            t = TaskModel.objects.create(
                id=uuid.uuid4(), tenant=tenant,
                project=project, code=f"T-{i:03d}", title=title,
                task_type="task", status="not_started",
                sort_order=i, duration=10,
                planned_start=date.today(),
                planned_end=date.today() + timedelta(days=10),
            )
            tasks.append(t)
        return tasks

    def test_create_tasks(self, admin_client, project):
        """ادمین ۳ تسک ایجاد می‌کند."""
        titles = ["تحلیل نیازمندی‌ها", "طراحی معماری", "مستندسازی"]
        for i, title in enumerate(titles, 1):
            resp = admin_client.post(self.TASK_URL, {
                "project": str(project.pk),
                "code": f"T-{i:03d}",
                "title": title,
                "task_type": "task",
                "duration": 10,
                "planned_start": str(date.today()),
                "planned_end": str(date.today() + timedelta(days=10)),
            }, format="json")
            assert resp.status_code in (200, 201), f"Failed creating task {title}: {resp.data}"

    def test_assign_raci_roles(self, admin_client, four_tasks, project, employee_user):
        """سناریوی ۴.۳ — تخصیص نقش‌های RACI."""
        roles = [
            (four_tasks[0], "R"),  # تحلیل → Responsible
            (four_tasks[1], "A"),  # طراحی → Accountable
            (four_tasks[2], "I"),  # مستندسازی → Informed
            (four_tasks[3], "C"),  # بررسی فنی → Consulted
        ]
        for task, role in roles:
            resp = admin_client.post(self.RACI_URL, {
                "project": str(project.pk),
                "task": str(task.pk),
                "user": str(employee_user.pk),
                "role": role,
            }, format="json")
            assert resp.status_code in (200, 201), f"Failed RACI {role} for {task.title}: {resp.data}"

        # Verify matrix
        resp = admin_client.get(f"{self.RACI_URL}matrix/?project={project.pk}")
        assert resp.status_code == 200

    def test_create_fs_dependency(self, admin_client, four_tasks, project):
        """سناریوی ۴.۴ — وابستگی FS بین تسک ۱ و ۲."""
        resp = admin_client.post(self.DEP_URL, {
            "project": str(project.pk),
            "predecessor": str(four_tasks[0].pk),
            "successor": str(four_tasks[1].pk),
            "dependency_type": "FS",
            "lag_days": 0,
        }, format="json")
        assert resp.status_code in (200, 201), resp.data


# ═══════════════════════════════════════════════════════════
# 4.4 + 5.3: اعتبارسنجی وابستگی FS و جریان پیشرفت
# ═══════════════════════════════════════════════════════════

class TestDependencyValidation:
    """سناریوی ۴.۴ و ۷ — اعتبارسنجی وابستگی FS."""

    TASK_URL = "/api/v1/pm/tasks/"

    @pytest.fixture
    def project(self, tenant, admin_user):
        return ProjectModel.objects.create(
            id=uuid.uuid4(), tenant=tenant,
            code="PRJ-DEP", title="پروژه وابستگی",
            project_type="project", status="active",
            priority="medium", manager=admin_user,
            planned_start=date.today(),
            planned_end=date.today() + timedelta(days=90),
            actual_start=date.today(),
        )

    @pytest.fixture
    def tasks_with_dep(self, tenant, project):
        """تسک A و B با وابستگی FS."""
        task_a = TaskModel.objects.create(
            id=uuid.uuid4(), tenant=tenant,
            project=project, code="T-001", title="تحلیل نیازمندی‌ها",
            task_type="task", status="not_started",
            sort_order=1, duration=10,
        )
        task_b = TaskModel.objects.create(
            id=uuid.uuid4(), tenant=tenant,
            project=project, code="T-002", title="طراحی معماری",
            task_type="task", status="not_started",
            sort_order=2, duration=10,
        )
        DependencyModel.objects.create(
            id=uuid.uuid4(), tenant=tenant,
            project=project,
            predecessor=task_a, successor=task_b,
            dependency_type="FS",
        )
        return task_a, task_b

    def test_cannot_update_progress_before_predecessor(self, admin_client, tasks_with_dep):
        """تلاش برای ثبت پیشرفت تسک B قبل از تکمیل A → خطا."""
        _, task_b = tasks_with_dep
        resp = admin_client.post(
            f"{self.TASK_URL}{task_b.pk}/update-progress/",
            {"progress": 10}, format="json",
        )
        assert resp.status_code == 422, f"Expected 422 but got {resp.status_code}: {resp.data}"
        assert "DEPENDENCY_NOT_SATISFIED" in str(resp.data)

    def test_cannot_start_before_predecessor(self, admin_client, tasks_with_dep):
        """تلاش برای شروع تسک B قبل از تکمیل A → خطا."""
        _, task_b = tasks_with_dep
        resp = admin_client.post(
            f"{self.TASK_URL}{task_b.pk}/change-status/",
            {"status": "in_progress"}, format="json",
        )
        assert resp.status_code == 422, f"Expected 422 but got {resp.status_code}: {resp.data}"
        assert "DEPENDENCY_NOT_SATISFIED" in str(resp.data)

    def test_can_start_after_predecessor_completed(self, admin_client, tasks_with_dep):
        """پس از تکمیل A، تسک B قابل شروع است."""
        task_a, task_b = tasks_with_dep
        # Complete task A
        task_a.status = "in_progress"
        task_a.save()
        resp = admin_client.post(
            f"{self.TASK_URL}{task_a.pk}/change-status/",
            {"status": "completed"}, format="json",
        )
        assert resp.status_code == 200, resp.data

        # Now B should be startable
        resp = admin_client.post(
            f"{self.TASK_URL}{task_b.pk}/change-status/",
            {"status": "in_progress"}, format="json",
        )
        assert resp.status_code == 200, f"Expected 200 but got {resp.status_code}: {resp.data}"

    def test_can_update_progress_after_predecessor_completed(self, admin_client, tasks_with_dep):
        """پس از تکمیل A، ثبت پیشرفت B ممکن است."""
        task_a, task_b = tasks_with_dep
        # Complete task A
        task_a.status = "completed"
        task_a.actual_end = date.today()
        task_a.progress = 100
        task_a.save()

        # Update B progress
        resp = admin_client.post(
            f"{self.TASK_URL}{task_b.pk}/update-progress/",
            {"progress": 50}, format="json",
        )
        assert resp.status_code == 200, resp.data
        assert resp.data["progress"] == 50


# ═══════════════════════════════════════════════════════════
# 5.1: کارتابل کارمند (my-tasks)
# ═══════════════════════════════════════════════════════════

class TestMyTasks:
    """سناریوی ۵.۱ — مشاهده تسک‌های محوله در کارتابل."""

    TASK_URL = "/api/v1/pm/tasks/"
    RACI_URL = "/api/v1/pm/raci/"

    @pytest.fixture
    def project_with_raci(self, tenant, admin_user, employee_user):
        """پروژه با ۴ تسک و RACI."""
        project = ProjectModel.objects.create(
            id=uuid.uuid4(), tenant=tenant,
            code="PRJ-MYTASKS", title="پروژه کارتابل",
            project_type="project", status="active",
            priority="medium", manager=admin_user,
        )
        titles_roles = [
            ("تحلیل نیازمندی‌ها", "R"),
            ("طراحی معماری", "A"),
            ("مستندسازی", "I"),
            ("بررسی فنی", "C"),
        ]
        tasks = []
        for i, (title, role) in enumerate(titles_roles, 1):
            task = TaskModel.objects.create(
                id=uuid.uuid4(), tenant=tenant,
                project=project, code=f"T-{i:03d}", title=title,
                task_type="task", status="not_started",
                sort_order=i, duration=10,
            )
            tasks.append(task)
            RACIAssignmentModel.objects.create(
                id=uuid.uuid4(), tenant=tenant,
                project=project, task=task,
                user=employee_user, role=role,
            )
        return project, tasks

    def test_employee_sees_assigned_tasks(self, employee_client, project_with_raci):
        """کارمند ۴ تسک RACI خود را می‌بیند."""
        resp = employee_client.get(f"{self.TASK_URL}my-tasks/")
        assert resp.status_code == 200, resp.data
        data = resp.data.get("data", resp.data)
        assert len(data) == 4

    def test_employee_sees_raci_roles(self, employee_client, project_with_raci):
        """هر تسک نشانگر نقش RACI دارد."""
        resp = employee_client.get(f"{self.TASK_URL}my-tasks/")
        data = resp.data.get("data", resp.data)
        roles_found = set()
        for task_data in data:
            for role in task_data.get("raci_roles", []):
                roles_found.add(role)
        assert roles_found == {"R", "A", "I", "C"}

    def test_admin_has_empty_my_tasks(self, admin_client, project_with_raci):
        """ادمین بدون RACI، کارتابل خالی دارد."""
        resp = admin_client.get(f"{self.TASK_URL}my-tasks/")
        assert resp.status_code == 200
        data = resp.data.get("data", resp.data)
        assert len(data) == 0


# ═══════════════════════════════════════════════════════════
# 5.3: تعامل بر اساس نقش RACI — پیشرفت و کامنت
# ═══════════════════════════════════════════════════════════

class TestRACIWorkflow:
    """سناریوی ۵.۳ — جریان کار RACI."""

    TASK_URL = "/api/v1/pm/tasks/"
    COMMENT_URL = "/api/v1/pm/comments/"

    @pytest.fixture
    def project_and_tasks(self, tenant, admin_user, employee_user):
        project = ProjectModel.objects.create(
            id=uuid.uuid4(), tenant=tenant,
            code="PRJ-FLOW", title="پروژه جریان کار",
            project_type="project", status="active",
            priority="medium", manager=admin_user,
            planned_start=date.today(),
            planned_end=date.today() + timedelta(days=90),
            actual_start=date.today(),
        )
        task1 = TaskModel.objects.create(
            id=uuid.uuid4(), tenant=tenant,
            project=project, code="T-001", title="تحلیل نیازمندی‌ها",
            task_type="task", status="not_started",
            sort_order=1, duration=10,
        )
        task2 = TaskModel.objects.create(
            id=uuid.uuid4(), tenant=tenant,
            project=project, code="T-002", title="طراحی معماری",
            task_type="task", status="not_started",
            sort_order=2, duration=15,
        )
        task3 = TaskModel.objects.create(
            id=uuid.uuid4(), tenant=tenant,
            project=project, code="T-003", title="مستندسازی",
            task_type="task", status="not_started",
            sort_order=3, duration=5,
        )
        task4 = TaskModel.objects.create(
            id=uuid.uuid4(), tenant=tenant,
            project=project, code="T-004", title="بررسی فنی",
            task_type="task", status="not_started",
            sort_order=4, duration=10,
        )

        # RACI assignments
        for task, role in [(task1, "R"), (task2, "A"), (task3, "I"), (task4, "C")]:
            RACIAssignmentModel.objects.create(
                id=uuid.uuid4(), tenant=tenant,
                project=project, task=task,
                user=employee_user, role=role,
            )

        # FS dependency: task1 → task2
        DependencyModel.objects.create(
            id=uuid.uuid4(), tenant=tenant,
            project=project,
            predecessor=task1, successor=task2,
            dependency_type="FS",
        )

        return project, task1, task2, task3, task4

    def test_responsible_updates_progress(self, admin_client, project_and_tasks):
        """نقش R — ثبت پیشرفت ۵۰٪."""
        _, task1, _, _, _ = project_and_tasks
        resp = admin_client.post(
            f"{self.TASK_URL}{task1.pk}/update-progress/",
            {"progress": 50}, format="json",
        )
        assert resp.status_code == 200, resp.data
        assert resp.data["progress"] == 50
        assert resp.data["status"] == "in_progress"

    def test_responsible_completes_task(self, admin_client, project_and_tasks):
        """نقش R — تکمیل تسک ۱ → آزادسازی تسک ۲."""
        _, task1, task2, _, _ = project_and_tasks

        # Start and complete task 1
        resp = admin_client.post(
            f"{self.TASK_URL}{task1.pk}/update-progress/",
            {"progress": 100}, format="json",
        )
        assert resp.status_code == 200
        assert resp.data["status"] == "completed"

        # Now task 2 should be startable
        resp = admin_client.post(
            f"{self.TASK_URL}{task2.pk}/change-status/",
            {"status": "in_progress"}, format="json",
        )
        assert resp.status_code == 200

    def test_consulted_adds_comment(self, employee_client, project_and_tasks):
        """نقش C — ثبت کامنت مشورتی."""
        project, _, _, _, task4 = project_and_tasks
        resp = employee_client.post(self.COMMENT_URL, {
            "project": str(project.pk),
            "task": str(task4.pk),
            "content": "پیشنهاد می‌شود از الگوی X استفاده شود",
        }, format="json")
        assert resp.status_code in (200, 201), resp.data


# ═══════════════════════════════════════════════════════════
# 6: پیشرفت پروژه — میانگین وزنی
# ═══════════════════════════════════════════════════════════

class TestProjectProgress:
    """سناریوی ۶ — محاسبه پیشرفت پروژه."""

    PROJECT_URL = "/api/v1/pm/projects/"
    TASK_URL = "/api/v1/pm/tasks/"

    @pytest.fixture
    def project_with_tasks(self, tenant, admin_user):
        project = ProjectModel.objects.create(
            id=uuid.uuid4(), tenant=tenant,
            code="PRJ-PROG", title="پروژه پیشرفت",
            project_type="project", status="active",
            priority="medium", manager=admin_user,
            planned_start=date.today(),
            planned_end=date.today() + timedelta(days=90),
            actual_start=date.today(),
        )
        # 3 tasks: durations 10, 20, 10 (total=40)
        t1 = TaskModel.objects.create(
            id=uuid.uuid4(), tenant=tenant,
            project=project, code="T-1", title="تسک ۱",
            task_type="task", status="not_started",
            sort_order=1, duration=10,
        )
        t2 = TaskModel.objects.create(
            id=uuid.uuid4(), tenant=tenant,
            project=project, code="T-2", title="تسک ۲",
            task_type="task", status="not_started",
            sort_order=2, duration=20,
        )
        t3 = TaskModel.objects.create(
            id=uuid.uuid4(), tenant=tenant,
            project=project, code="T-3", title="تسک ۳",
            task_type="task", status="not_started",
            sort_order=3, duration=10,
        )
        return project, t1, t2, t3

    def test_project_progress_weighted_average(self, admin_client, project_with_tasks):
        """پیشرفت پروژه = میانگین وزنی بر اساس duration."""
        project, t1, t2, t3 = project_with_tasks

        # t1: progress=100, duration=10 → 1000
        admin_client.post(
            f"{self.TASK_URL}{t1.pk}/update-progress/",
            {"progress": 100}, format="json",
        )
        # t2: progress=50, duration=20 → 1000
        admin_client.post(
            f"{self.TASK_URL}{t2.pk}/update-progress/",
            {"progress": 50}, format="json",
        )
        # t3: progress=0, duration=10 → 0
        # Total weighted = 2000, total duration = 40
        # Expected: 2000/40 = 50

        project.refresh_from_db()
        assert project.progress == 50

    def test_recalculate_progress_action(self, admin_client, project_with_tasks):
        """اکشن recalculate-progress پروژه."""
        project, t1, t2, t3 = project_with_tasks

        # Set progress directly
        t1.progress = 100
        t1.status = "completed"
        t1.save()
        t2.progress = 50
        t2.status = "in_progress"
        t2.save()

        resp = admin_client.post(
            f"{self.PROJECT_URL}{project.pk}/recalculate-progress/",
        )
        assert resp.status_code == 200
        assert resp.data["data"]["progress"] == 50


# ═══════════════════════════════════════════════════════════
# 7: سناریوهای مرزی
# ═══════════════════════════════════════════════════════════

class TestEdgeCases:
    """سناریوی ۷ — حالات مرزی."""

    TASK_URL = "/api/v1/pm/tasks/"
    DEP_URL = "/api/v1/pm/dependencies/"

    @pytest.fixture
    def project(self, tenant, admin_user):
        return ProjectModel.objects.create(
            id=uuid.uuid4(), tenant=tenant,
            code="PRJ-EDGE", title="پروژه مرزی",
            project_type="project", status="active",
            priority="medium", manager=admin_user,
        )

    @pytest.fixture
    def task_with_deps(self, tenant, project):
        task_a = TaskModel.objects.create(
            id=uuid.uuid4(), tenant=tenant,
            project=project, code="T-A", title="تسک A",
            task_type="task", status="not_started", sort_order=1, duration=5,
        )
        task_b = TaskModel.objects.create(
            id=uuid.uuid4(), tenant=tenant,
            project=project, code="T-B", title="تسک B",
            task_type="task", status="not_started", sort_order=2, duration=5,
        )
        DependencyModel.objects.create(
            id=uuid.uuid4(), tenant=tenant,
            project=project,
            predecessor=task_a, successor=task_b,
            dependency_type="FS",
        )
        return task_a, task_b

    def test_delete_task_with_dependency_blocked(self, admin_client, task_with_deps):
        """ردیف ۴ — حذف تسکی که وابستگی دارد → خطا."""
        task_a, _ = task_with_deps
        resp = admin_client.delete(f"{self.TASK_URL}{task_a.pk}/")
        assert resp.status_code == 400, f"Expected 400 but got {resp.status_code}: {resp.data}"

    def test_unauthenticated_access_denied(self, unauthenticated_client, task_with_deps):
        """دسترسی غیرمجاز → 401/403."""
        task_a, _ = task_with_deps
        resp = unauthenticated_client.get(f"{self.TASK_URL}{task_a.pk}/")
        assert resp.status_code in (401, 403)

    def test_progress_zero_allowed_without_dependency(self, admin_client, task_with_deps):
        """ثبت پیشرفت ۰ روی تسک وابسته مجاز است (بدون شروع)."""
        _, task_b = task_with_deps
        resp = admin_client.post(
            f"{self.TASK_URL}{task_b.pk}/update-progress/",
            {"progress": 0}, format="json",
        )
        assert resp.status_code == 200


# ═══════════════════════════════════════════════════════════
# 8: لاگ فعالیت
# ═══════════════════════════════════════════════════════════

class TestActivityLogging:
    """تست ثبت لاگ فعالیت."""

    TASK_URL = "/api/v1/pm/tasks/"
    LOG_URL = "/api/v1/pm/activity-logs/"

    @pytest.fixture
    def project_task(self, tenant, admin_user):
        project = ProjectModel.objects.create(
            id=uuid.uuid4(), tenant=tenant,
            code="PRJ-LOG", title="پروژه لاگ",
            project_type="project", status="active",
            priority="medium", manager=admin_user,
        )
        task = TaskModel.objects.create(
            id=uuid.uuid4(), tenant=tenant,
            project=project, code="T-LOG", title="تسک لاگ",
            task_type="task", status="not_started",
            sort_order=1, duration=10,
        )
        return project, task

    def test_progress_update_creates_log(self, admin_client, project_task):
        """ثبت پیشرفت باید لاگ فعالیت ایجاد کند."""
        project, task = project_task
        admin_client.post(
            f"{self.TASK_URL}{task.pk}/update-progress/",
            {"progress": 30}, format="json",
        )
        logs = ActivityLogModel.objects.filter(
            project=project, task=task, action="progress_updated",
        )
        assert logs.exists()

    def test_status_change_creates_log(self, admin_client, project_task):
        """تغییر وضعیت باید لاگ فعالیت ایجاد کند."""
        project, task = project_task
        admin_client.post(
            f"{self.TASK_URL}{task.pk}/change-status/",
            {"status": "in_progress"}, format="json",
        )
        logs = ActivityLogModel.objects.filter(
            project=project, task=task, action="status_changed",
        )
        assert logs.exists()


# ═══════════════════════════════════════════════════════════
# Full E2E Scenario — جریان کامل
# ═══════════════════════════════════════════════════════════

class TestFullScenario:
    """
    تست E2E — جریان کامل سناریوی ۱.

    ۱. ادمین پروژه ایجاد می‌کند
    ۲. تسک‌ها ایجاد می‌شوند
    ۳. RACI تخصیص می‌شود
    ۴. وابستگی FS ایجاد می‌شود
    ۵. تسک B قبل از تکمیل A قابل شروع نیست
    ۶. تسک A تکمیل می‌شود
    ۷. تسک B شروع و پیشرفت ثبت می‌شود
    ۸. کارمند تسک‌های RACI خود را می‌بیند
    ۹. پیشرفت پروژه محاسبه می‌شود
    """

    PROJECT_URL = "/api/v1/pm/projects/"
    TASK_URL = "/api/v1/pm/tasks/"
    RACI_URL = "/api/v1/pm/raci/"
    DEP_URL = "/api/v1/pm/dependencies/"
    COMMENT_URL = "/api/v1/pm/comments/"

    def test_full_workflow(self, admin_client, employee_client, tenant, admin_user, employee_user):
        # ۱. ادمین پروژه ایجاد می‌کند
        resp = admin_client.post(self.PROJECT_URL, {
            "code": "PRJ-E2E",
            "title": "پروژه نمونه",
            "description": "تست سناریوی RACI",
            "project_type": "project",
            "priority": "medium",
            "planned_start": str(date.today()),
            "planned_end": str(date.today() + timedelta(days=90)),
        }, format="json")
        assert resp.status_code in (200, 201), f"Step 1 failed: {resp.data}"
        project_id = resp.data["id"]

        # Activate project: draft → planning → active
        resp = admin_client.post(
            f"{self.PROJECT_URL}{project_id}/change-status/",
            {"status": "planning"}, format="json",
        )
        assert resp.status_code == 200, f"Step 1b (draft→planning) failed: {resp.data}"
        resp = admin_client.post(
            f"{self.PROJECT_URL}{project_id}/change-status/",
            {"status": "active"}, format="json",
        )
        assert resp.status_code == 200, f"Step 1c (planning→active) failed: {resp.data}"

        # ۲. ایجاد ۴ تسک
        task_ids = []
        titles = ["تحلیل نیازمندی‌ها", "طراحی معماری", "مستندسازی", "بررسی فنی"]
        for i, title in enumerate(titles, 1):
            resp = admin_client.post(self.TASK_URL, {
                "project": project_id,
                "code": f"T-{i:03d}",
                "title": title,
                "task_type": "task",
                "duration": 10,
                "planned_start": str(date.today()),
                "planned_end": str(date.today() + timedelta(days=10)),
            }, format="json")
            assert resp.status_code in (200, 201), f"Step 2 task {i} failed: {resp.data}"
            task_ids.append(resp.data["id"])

        # ۳. تخصیص RACI
        roles = ["R", "A", "I", "C"]
        for task_id, role in zip(task_ids, roles):
            resp = admin_client.post(self.RACI_URL, {
                "project": project_id,
                "task": task_id,
                "user": str(employee_user.pk),
                "role": role,
            }, format="json")
            assert resp.status_code in (200, 201), f"Step 3 RACI {role} failed: {resp.data}"

        # ۴. وابستگی FS: تسک ۱ → تسک ۲
        resp = admin_client.post(self.DEP_URL, {
            "project": project_id,
            "predecessor": task_ids[0],
            "successor": task_ids[1],
            "dependency_type": "FS",
            "lag_days": 0,
        }, format="json")
        assert resp.status_code in (200, 201), f"Step 4 failed: {resp.data}"

        # ۵. تسک B قبل از تکمیل A → خطا
        resp = admin_client.post(
            f"{self.TASK_URL}{task_ids[1]}/update-progress/",
            {"progress": 10}, format="json",
        )
        assert resp.status_code == 422, f"Step 5 should fail: {resp.data}"

        # ۶. تکمیل تسک A
        resp = admin_client.post(
            f"{self.TASK_URL}{task_ids[0]}/update-progress/",
            {"progress": 50}, format="json",
        )
        assert resp.status_code == 200, f"Step 6a failed: {resp.data}"

        resp = admin_client.post(
            f"{self.TASK_URL}{task_ids[0]}/update-progress/",
            {"progress": 100}, format="json",
        )
        assert resp.status_code == 200, f"Step 6b failed: {resp.data}"

        # ۷. حالا تسک B قابل شروع
        resp = admin_client.post(
            f"{self.TASK_URL}{task_ids[1]}/update-progress/",
            {"progress": 50}, format="json",
        )
        assert resp.status_code == 200, f"Step 7 failed: {resp.data}"

        # ۸. کارمند تسک‌های RACI خود را می‌بیند
        resp = employee_client.get(f"{self.TASK_URL}my-tasks/")
        assert resp.status_code == 200
        data = resp.data.get("data", resp.data)
        assert len(data) == 4, f"Step 8 expected 4 tasks, got {len(data)}"

        # ۹. پیشرفت پروژه
        # t1: 100% * 10 = 1000
        # t2: 50% * 10 = 500
        # t3: 0% * 10 = 0
        # t4: 0% * 10 = 0
        # Total = 1500 / 40 = 38 (rounded)
        project_obj = ProjectModel.objects.get(id=project_id)
        assert project_obj.progress == 38, f"Step 9: expected 38, got {project_obj.progress}"

        # ۱۰. کامنت مشورتی از کارمند (C role)
        resp = employee_client.post(self.COMMENT_URL, {
            "project": project_id,
            "task": task_ids[3],
            "content": "پیشنهاد می‌شود از الگوی X استفاده شود",
        }, format="json")
        assert resp.status_code in (200, 201), f"Step 10 failed: {resp.data}"

        # ۱۱. بررسی لاگ‌های فعالیت
        logs = ActivityLogModel.objects.filter(project_id=project_id)
        assert logs.count() >= 3, f"Step 11: expected >=3 logs, got {logs.count()}"
