"""
PM Module — Domain Unit Tests.

تست‌های واحد برای اجزای حوزه‌ای ماژول مدیریت پروژه:
- Value Objects (DateRange, WBSCode, Percentage, Duration, CostRate, EVMMetrics, RiskScore)
- Entities (Project, Task, Dependency, Budget, CostEntry)
- Domain Services (CriticalPathService, EVMService, WBSService)
- Domain Exceptions
"""
import uuid
from datetime import date, timedelta
from decimal import Decimal

import pytest

from modules.pm.backend.domain.value_objects.common import (
    ProjectStatus, ProjectPriority, ProjectType,
    TaskStatus, TaskType, DependencyType,
    ResourceType, CostType,
    RiskProbability, RiskImpact, RiskStatus, RiskResponseStrategy,
    ChangeRequestStatus, TimesheetStatus, BaselineType, MemberRole,
    DateRange, WBSCode, Percentage, Duration, CostRate, EVMMetrics, RiskScore,
)
from modules.pm.backend.domain.entities.project import Project
from modules.pm.backend.domain.entities.task import Task, Dependency
from modules.pm.backend.domain.entities.cost import Budget, CostEntry
from modules.pm.backend.domain.services.scheduling import (
    CriticalPathService, EVMService, WBSService,
)
from modules.pm.backend.domain.exceptions.pm_exceptions import (
    PMDomainException,
    ProjectNotFoundException,
    TaskNotFoundException,
    InvalidStatusTransitionException,
    CircularDependencyException,
    ResourceOverallocationException,
    BudgetExceededException,
    BaselineActiveException,
)


# ═══════════════════════════════════════════════════════════
# 1) Value Objects
# ═══════════════════════════════════════════════════════════

class TestDateRange:
    def test_valid_range(self):
        dr = DateRange(start_date=date(2025, 1, 1), end_date=date(2025, 1, 31))
        assert dr.duration_days == 31

    def test_single_day(self):
        dr = DateRange(start_date=date(2025, 3, 1), end_date=date(2025, 3, 1))
        assert dr.duration_days == 1

    def test_invalid_range_raises(self):
        with pytest.raises(ValueError, match="تاریخ شروع"):
            DateRange(start_date=date(2025, 2, 1), end_date=date(2025, 1, 1))

    def test_business_days(self):
        # Mon-Sun = 7 days, 6 business days (excluding Friday)
        dr = DateRange(start_date=date(2025, 6, 7), end_date=date(2025, 6, 13))
        assert dr.duration_business_days == 6  # Fri excluded

    def test_overlaps_true(self):
        a = DateRange(start_date=date(2025, 1, 1), end_date=date(2025, 1, 15))
        b = DateRange(start_date=date(2025, 1, 10), end_date=date(2025, 1, 20))
        assert a.overlaps(b) is True

    def test_overlaps_false(self):
        a = DateRange(start_date=date(2025, 1, 1), end_date=date(2025, 1, 10))
        b = DateRange(start_date=date(2025, 1, 11), end_date=date(2025, 1, 20))
        assert a.overlaps(b) is False

    def test_contains(self):
        dr = DateRange(start_date=date(2025, 1, 1), end_date=date(2025, 1, 31))
        assert dr.contains(date(2025, 1, 15)) is True
        assert dr.contains(date(2025, 2, 1)) is False


class TestWBSCode:
    def test_valid_code(self):
        wbs = WBSCode(code="1.2.3")
        assert wbs.level == 3
        assert wbs.parent_code == "1.2"

    def test_root_code(self):
        wbs = WBSCode(code="1")
        assert wbs.level == 1
        assert wbs.parent_code is None

    def test_empty_code_raises(self):
        with pytest.raises(ValueError, match="خالی"):
            WBSCode(code="")

    def test_non_numeric_raises(self):
        with pytest.raises(ValueError, match="عدد"):
            WBSCode(code="1.A.3")

    def test_is_child_of(self):
        parent = WBSCode(code="1.2")
        child = WBSCode(code="1.2.3")
        assert child.is_child_of(parent) is True
        assert parent.is_child_of(child) is False


class TestPercentage:
    def test_valid(self):
        p = Percentage(value=50)
        assert p.value == 50

    def test_zero(self):
        p = Percentage(value=0)
        assert p.value == 0

    def test_hundred(self):
        p = Percentage(value=100)
        assert p.value == 100

    def test_out_of_range(self):
        with pytest.raises(ValueError):
            Percentage(value=101)
        with pytest.raises(ValueError):
            Percentage(value=-1)


class TestDuration:
    def test_valid(self):
        d = Duration(days=5)
        assert d.to_hours() == 40

    def test_custom_hours(self):
        d = Duration(days=3)
        assert d.to_hours(hours_per_day=10) == 30

    def test_negative_raises(self):
        with pytest.raises(ValueError):
            Duration(days=-1)


class TestCostRate:
    def test_valid(self):
        cr = CostRate(standard_rate=Decimal("500000"), overtime_rate=Decimal("750000"))
        assert cr.currency == "IRR"

    def test_negative_rate_raises(self):
        with pytest.raises(ValueError):
            CostRate(standard_rate=Decimal("-1"), overtime_rate=Decimal("0"))
        with pytest.raises(ValueError):
            CostRate(standard_rate=Decimal("0"), overtime_rate=Decimal("-1"))


class TestEVMMetrics:
    def test_cost_variance(self):
        evm = EVMMetrics(
            planned_value=Decimal("100"),
            earned_value=Decimal("80"),
            actual_cost=Decimal("90"),
            budget_at_completion=Decimal("200"),
        )
        assert evm.cost_variance == Decimal("-10")  # EV - AC = 80 - 90
        assert evm.schedule_variance == Decimal("-20")  # EV - PV = 80 - 100

    def test_performance_indices(self):
        evm = EVMMetrics(
            planned_value=Decimal("100"),
            earned_value=Decimal("80"),
            actual_cost=Decimal("100"),
            budget_at_completion=Decimal("200"),
        )
        cpi = evm.cost_performance_index
        spi = evm.schedule_performance_index
        assert cpi == Decimal("0.8")   # 80/100
        assert spi == Decimal("0.8")   # 80/100

    def test_zero_actual_cost_cpi(self):
        evm = EVMMetrics(
            planned_value=Decimal("100"),
            earned_value=Decimal("50"),
            actual_cost=Decimal("0"),
            budget_at_completion=Decimal("200"),
        )
        assert evm.cost_performance_index is None

    def test_eac_etc_vac(self):
        evm = EVMMetrics(
            planned_value=Decimal("100"),
            earned_value=Decimal("80"),
            actual_cost=Decimal("100"),
            budget_at_completion=Decimal("200"),
        )
        eac = evm.estimate_at_completion  # BAC / CPI = 200 / 0.8 = 250
        assert eac == Decimal("250")
        etc = evm.estimate_to_complete  # EAC - AC = 250 - 100 = 150
        assert etc == Decimal("150")
        vac = evm.variance_at_completion  # BAC - EAC = 200 - 250 = -50
        assert vac == Decimal("-50")

    def test_tcpi(self):
        evm = EVMMetrics(
            planned_value=Decimal("100"),
            earned_value=Decimal("80"),
            actual_cost=Decimal("100"),
            budget_at_completion=Decimal("200"),
        )
        tcpi = evm.to_complete_performance_index  # (BAC - EV) / (BAC - AC) = 120/100
        assert tcpi == Decimal("1.2")


class TestRiskScore:
    def test_score_calculation(self):
        rs = RiskScore(probability=RiskProbability.HIGH, impact=RiskImpact.MAJOR)
        assert rs.score == 16  # 4 * 4

    def test_critical_level(self):
        rs = RiskScore(probability=RiskProbability.VERY_HIGH, impact=RiskImpact.CRITICAL)
        assert rs.score == 25
        assert rs.level == "critical"

    def test_low_level(self):
        rs = RiskScore(probability=RiskProbability.VERY_LOW, impact=RiskImpact.MINOR)
        assert rs.score == 2
        assert rs.level == "low"

    def test_medium_level(self):
        rs = RiskScore(probability=RiskProbability.MEDIUM, impact=RiskImpact.MINOR)
        assert rs.score == 6
        assert rs.level == "medium"


# ═══════════════════════════════════════════════════════════
# 2) Entities
# ═══════════════════════════════════════════════════════════

class TestProjectEntity:
    def _make_project(self, **kwargs):
        defaults = {
            "id": uuid.uuid4(),
            "tenant_id": uuid.uuid4(),
            "code": "PRJ-001",
            "title": "پروژه تست",
            "status": ProjectStatus.DRAFT,
        }
        defaults.update(kwargs)
        return Project(**defaults)

    def test_create_project(self):
        p = self._make_project()
        assert p.status == ProjectStatus.DRAFT
        assert p.progress == 0

    def test_can_start(self):
        p = self._make_project(
            status=ProjectStatus.PLANNING,
            planned_start=date.today(),
        )
        assert p.can_start() is True

    def test_cannot_start_from_draft(self):
        p = self._make_project(status=ProjectStatus.DRAFT, planned_start=date.today())
        assert p.can_start() is False

    def test_start(self):
        p = self._make_project(
            status=ProjectStatus.PLANNING,
            planned_start=date.today(),
        )
        p.start()
        assert p.status == ProjectStatus.ACTIVE
        assert p.actual_start == date.today()

    def test_start_raises_for_invalid_status(self):
        p = self._make_project(status=ProjectStatus.DRAFT)
        with pytest.raises(ValueError):
            p.start()

    def test_complete(self):
        p = self._make_project(status=ProjectStatus.ACTIVE)
        p.complete()
        assert p.status == ProjectStatus.COMPLETED
        assert p.progress == 100
        assert p.actual_end == date.today()

    def test_complete_raises_for_non_active(self):
        p = self._make_project(status=ProjectStatus.DRAFT)
        with pytest.raises(ValueError):
            p.complete()

    def test_cancel(self):
        p = self._make_project(status=ProjectStatus.ACTIVE)
        p.cancel()
        assert p.status == ProjectStatus.CANCELLED

    def test_cancel_raises_for_completed(self):
        p = self._make_project(status=ProjectStatus.COMPLETED)
        with pytest.raises(ValueError):
            p.cancel()

    def test_put_on_hold(self):
        p = self._make_project(status=ProjectStatus.ACTIVE)
        p.put_on_hold()
        assert p.status == ProjectStatus.ON_HOLD

    def test_resume(self):
        p = self._make_project(status=ProjectStatus.ON_HOLD)
        p.resume()
        assert p.status == ProjectStatus.ACTIVE

    def test_update_progress(self):
        p = self._make_project()
        p.update_progress(50)
        assert p.progress == 50

    def test_update_progress_invalid(self):
        p = self._make_project()
        with pytest.raises(ValueError):
            p.update_progress(101)
        with pytest.raises(ValueError):
            p.update_progress(-1)

    def test_is_overdue(self):
        p = self._make_project(
            status=ProjectStatus.ACTIVE,
            planned_end=date.today() - timedelta(days=1),
        )
        assert p.is_overdue is True

    def test_not_overdue(self):
        p = self._make_project(
            status=ProjectStatus.ACTIVE,
            planned_end=date.today() + timedelta(days=10),
        )
        assert p.is_overdue is False

    def test_budget_variance(self):
        p = self._make_project(
            total_budget=Decimal("100"),
            actual_cost=Decimal("80"),
        )
        assert p.budget_variance == Decimal("20")

    def test_planned_duration(self):
        p = self._make_project(
            planned_start=date(2025, 1, 1),
            planned_end=date(2025, 1, 31),
        )
        assert p.planned_duration == 31


class TestTaskEntity:
    def _make_task(self, **kwargs):
        defaults = {
            "id": uuid.uuid4(),
            "tenant_id": uuid.uuid4(),
            "project_id": uuid.uuid4(),
            "code": "T-001",
            "title": "تسک تست",
            "status": TaskStatus.NOT_STARTED,
        }
        defaults.update(kwargs)
        return Task(**defaults)

    def test_start_work(self):
        t = self._make_task()
        t.start_work()
        assert t.status == TaskStatus.IN_PROGRESS
        assert t.actual_start == date.today()

    def test_start_work_from_ready(self):
        t = self._make_task(status=TaskStatus.READY)
        t.start_work()
        assert t.status == TaskStatus.IN_PROGRESS

    def test_start_work_already_started(self):
        t = self._make_task(status=TaskStatus.IN_PROGRESS)
        with pytest.raises(ValueError):
            t.start_work()

    def test_mark_ready(self):
        t = self._make_task()
        t.mark_ready()
        assert t.status == TaskStatus.READY

    def test_mark_ready_invalid(self):
        t = self._make_task(status=TaskStatus.IN_PROGRESS)
        with pytest.raises(ValueError):
            t.mark_ready()

    def test_hold(self):
        t = self._make_task(status=TaskStatus.IN_PROGRESS)
        t.hold()
        assert t.status == TaskStatus.ON_HOLD

    def test_resume(self):
        t = self._make_task(status=TaskStatus.ON_HOLD)
        t.resume()
        assert t.status == TaskStatus.IN_PROGRESS

    def test_cancel(self):
        t = self._make_task(status=TaskStatus.IN_PROGRESS)
        t.cancel()
        assert t.status == TaskStatus.CANCELLED

    def test_cancel_completed_fails(self):
        t = self._make_task(status=TaskStatus.COMPLETED)
        with pytest.raises(ValueError):
            t.cancel()

    def test_complete(self):
        t = self._make_task(status=TaskStatus.IN_PROGRESS)
        t.complete()
        assert t.status == TaskStatus.COMPLETED
        assert t.progress == 100

    def test_complete_from_ready(self):
        t = self._make_task(status=TaskStatus.READY)
        t.complete()
        assert t.status == TaskStatus.COMPLETED

    def test_update_progress_auto_start(self):
        t = self._make_task()
        t.update_progress(10)
        assert t.status == TaskStatus.IN_PROGRESS

    def test_update_progress_auto_complete(self):
        t = self._make_task(status=TaskStatus.IN_PROGRESS)
        t.update_progress(100)
        assert t.status == TaskStatus.COMPLETED

    def test_is_overdue(self):
        t = self._make_task(
            status=TaskStatus.IN_PROGRESS,
            planned_end=date.today() - timedelta(days=1),
        )
        assert t.is_overdue is True

    def test_cost_variance(self):
        t = self._make_task(
            planned_cost=Decimal("100"),
            actual_cost=Decimal("120"),
        )
        assert t.cost_variance == Decimal("-20")


class TestDependencyEntity:
    def test_create_dependency(self):
        d = Dependency(
            id=uuid.uuid4(),
            tenant_id=uuid.uuid4(),
            project_id=uuid.uuid4(),
            predecessor_id=uuid.uuid4(),
            successor_id=uuid.uuid4(),
            dependency_type=DependencyType.FS,
            lag_days=2,
        )
        assert d.dependency_type == DependencyType.FS
        assert d.lag_days == 2

    def test_self_reference_raises(self):
        same_id = uuid.uuid4()
        d = Dependency(
            id=uuid.uuid4(),
            tenant_id=uuid.uuid4(),
            project_id=uuid.uuid4(),
            predecessor_id=same_id,
            successor_id=same_id,
        )
        with pytest.raises(ValueError, match="خودش"):
            d.validate()


class TestBudgetEntity:
    def test_remaining_budget(self):
        b = Budget(
            id=uuid.uuid4(),
            tenant_id=uuid.uuid4(),
            project_id=uuid.uuid4(),
            original_budget=Decimal("1000"),
            revised_budget=Decimal("1200"),
            actual_cost=Decimal("300"),
        )
        assert b.remaining_budget == Decimal("900")  # 1200 - 300

    def test_remaining_budget_no_revision(self):
        b = Budget(
            id=uuid.uuid4(),
            tenant_id=uuid.uuid4(),
            project_id=uuid.uuid4(),
            original_budget=Decimal("1000"),
            revised_budget=Decimal("0"),
            actual_cost=Decimal("300"),
        )
        assert b.remaining_budget == Decimal("700")  # 1000 - 300

    def test_budget_utilization(self):
        b = Budget(
            id=uuid.uuid4(),
            tenant_id=uuid.uuid4(),
            project_id=uuid.uuid4(),
            original_budget=Decimal("1000"),
            revised_budget=Decimal("0"),
            actual_cost=Decimal("500"),
        )
        assert b.budget_utilization == Decimal("50")


# ═══════════════════════════════════════════════════════════
# 3) Domain Services
# ═══════════════════════════════════════════════════════════

class TestCriticalPathService:
    def _make_task(self, **kwargs):
        defaults = {
            "id": uuid.uuid4(),
            "tenant_id": uuid.uuid4(),
            "project_id": uuid.uuid4(),
            "code": "T",
            "title": "Task",
            "task_type": TaskType.TASK,
            "status": TaskStatus.NOT_STARTED,
            "planned_start": date.today(),
            "planned_end": date.today() + timedelta(days=4),
            "duration": 5,
        }
        defaults.update(kwargs)
        return Task(**defaults)

    def test_empty_tasks(self):
        svc = CriticalPathService()
        result = svc.calculate([], [])
        assert result == []

    def test_single_task(self):
        svc = CriticalPathService()
        t = self._make_task()
        result = svc.calculate([t], [])
        assert len(result) == 1
        assert result[0].early_start is not None
        assert result[0].is_critical is True  # Only task = critical

    def test_two_sequential_tasks(self):
        svc = CriticalPathService()
        pid = uuid.uuid4()
        tid = uuid.uuid4()

        t1 = self._make_task(
            id=(id1 := uuid.uuid4()),
            project_id=pid,
            planned_start=date(2025, 1, 1),
            planned_end=date(2025, 1, 5),
            duration=5,
        )
        t2 = self._make_task(
            id=(id2 := uuid.uuid4()),
            project_id=pid,
            planned_start=date(2025, 1, 6),
            planned_end=date(2025, 1, 10),
            duration=5,
        )
        dep = Dependency(
            id=uuid.uuid4(),
            tenant_id=uuid.uuid4(),
            project_id=pid,
            predecessor_id=id1,
            successor_id=id2,
            dependency_type=DependencyType.FS,
            lag_days=0,
        )

        result = svc.calculate([t1, t2], [dep])
        assert len(result) == 2
        # Both should be critical in a linear chain
        for t in result:
            assert t.is_critical is True
            assert t.total_float == 0

    def test_parallel_tasks_float(self):
        svc = CriticalPathService()
        pid = uuid.uuid4()
        tid = uuid.uuid4()

        # Start → A (5 days) → End
        # Start → B (3 days) → End
        # A is critical, B has float
        t_a = self._make_task(
            id=(id_a := uuid.uuid4()),
            project_id=pid,
            planned_start=date(2025, 1, 1),
            planned_end=date(2025, 1, 5),
            duration=5,
        )
        t_b = self._make_task(
            id=(id_b := uuid.uuid4()),
            project_id=pid,
            planned_start=date(2025, 1, 1),
            planned_end=date(2025, 1, 3),
            duration=3,
        )

        result = svc.calculate([t_a, t_b], [])
        task_map = {t.id: t for t in result}

        assert task_map[id_a].is_critical is True
        assert task_map[id_b].is_critical is False
        assert task_map[id_b].total_float == 2

    def test_circular_dependency_raises(self):
        svc = CriticalPathService()
        pid = uuid.uuid4()
        tid = uuid.uuid4()

        t1 = self._make_task(id=(id1 := uuid.uuid4()), project_id=pid, duration=5)
        t2 = self._make_task(id=(id2 := uuid.uuid4()), project_id=pid, duration=5)

        # A → B and B → A = cycle
        dep1 = Dependency(
            id=uuid.uuid4(), tenant_id=tid, project_id=pid,
            predecessor_id=id1, successor_id=id2,
            dependency_type=DependencyType.FS,
        )
        dep2 = Dependency(
            id=uuid.uuid4(), tenant_id=tid, project_id=pid,
            predecessor_id=id2, successor_id=id1,
            dependency_type=DependencyType.FS,
        )

        with pytest.raises(CircularDependencyException):
            svc.calculate([t1, t2], [dep1, dep2])


class TestEVMService:
    def _make_task(self, **kwargs):
        defaults = {
            "id": uuid.uuid4(),
            "tenant_id": uuid.uuid4(),
            "project_id": uuid.uuid4(),
            "code": "T",
            "title": "Task",
            "task_type": TaskType.TASK,
            "status": TaskStatus.IN_PROGRESS,
            "planned_cost": Decimal("100"),
            "actual_cost": Decimal("0"),
            "progress": 0,
            "duration": 10,
        }
        defaults.update(kwargs)
        return Task(**defaults)

    def test_empty_tasks(self):
        svc = EVMService()
        result = svc.calculate_evm([], Decimal("1000"))
        assert result.planned_value == Decimal("0")
        assert result.earned_value == Decimal("0")

    def test_complete_task(self):
        svc = EVMService()
        t = self._make_task(
            planned_start=date.today() - timedelta(days=20),
            planned_end=date.today() - timedelta(days=10),
            planned_cost=Decimal("100"),
            actual_cost=Decimal("90"),
            progress=100,
        )
        result = svc.calculate_evm([t], Decimal("100"))
        assert result.earned_value == result.budget_at_completion  # 100% complete
        assert result.actual_cost == Decimal("90")

    def test_partial_progress(self):
        svc = EVMService()
        t = self._make_task(
            planned_start=date.today() - timedelta(days=5),
            planned_end=date.today() + timedelta(days=5),
            planned_cost=Decimal("100"),
            actual_cost=Decimal("60"),
            progress=50,
        )
        result = svc.calculate_evm([t], Decimal("100"))
        assert result.earned_value == Decimal("50")  # 50% of BAC


class TestWBSService:
    def _make_task(self, **kwargs):
        defaults = {
            "id": uuid.uuid4(),
            "tenant_id": uuid.uuid4(),
            "project_id": uuid.uuid4(),
            "code": "",
            "title": "Task",
            "task_type": TaskType.TASK,
            "status": TaskStatus.NOT_STARTED,
            "sort_order": 0,
            "parent_id": None,
        }
        defaults.update(kwargs)
        return Task(**defaults)

    def test_generate_wbs_codes(self):
        svc = WBSService()
        pid = uuid.uuid4()
        tid = uuid.uuid4()

        root = self._make_task(id=(root_id := uuid.uuid4()), sort_order=1, task_type=TaskType.SUMMARY)
        child1 = self._make_task(id=uuid.uuid4(), parent_id=root_id, sort_order=1)
        child2 = self._make_task(id=uuid.uuid4(), parent_id=root_id, sort_order=2)

        result = svc.generate_wbs_codes([root, child1, child2])
        code_map = {t.id: t.code for t in result}

        assert code_map[root_id] == "1"
        assert code_map[child1.id] == "1.1"
        assert code_map[child2.id] == "1.2"

    def test_summary_dates(self):
        svc = WBSService()
        pid = uuid.uuid4()

        parent = self._make_task(
            id=(parent_id := uuid.uuid4()),
            task_type=TaskType.SUMMARY,
        )
        child1 = self._make_task(
            parent_id=parent_id,
            planned_start=date(2025, 1, 1),
            planned_end=date(2025, 1, 10),
            planned_cost=Decimal("50"),
            actual_cost=Decimal("20"),
            progress=60,
        )
        child2 = self._make_task(
            parent_id=parent_id,
            planned_start=date(2025, 1, 5),
            planned_end=date(2025, 1, 20),
            planned_cost=Decimal("30"),
            actual_cost=Decimal("10"),
            progress=40,
        )

        result = svc.calculate_summary_dates([parent, child1, child2])
        parent_result = next(t for t in result if t.id == parent_id)

        assert parent_result.planned_start == date(2025, 1, 1)
        assert parent_result.planned_end == date(2025, 1, 20)
        assert parent_result.progress == 50  # avg(60, 40)
        assert parent_result.planned_cost == Decimal("80")
        assert parent_result.actual_cost == Decimal("30")


# ═══════════════════════════════════════════════════════════
# 4) Domain Exceptions
# ═══════════════════════════════════════════════════════════

class TestDomainExceptions:
    def test_project_not_found(self):
        ex = ProjectNotFoundException("some-id")
        assert "some-id" in str(ex)

    def test_task_not_found(self):
        ex = TaskNotFoundException()
        assert "تسک" in str(ex)

    def test_invalid_status_transition(self):
        ex = InvalidStatusTransitionException("active", "draft", "پروژه")
        assert "پروژه" in str(ex)

    def test_circular_dependency(self):
        ex = CircularDependencyException()
        assert "حلقوی" in str(ex)

    def test_resource_overallocation(self):
        ex = ResourceOverallocationException("منبع ۱")
        assert "منبع ۱" in str(ex)

    def test_budget_exceeded(self):
        ex = BudgetExceededException("بودجه اصلی")
        assert "بودجه اصلی" in str(ex)

    def test_baseline_active(self):
        ex = BaselineActiveException()
        assert "فعال" in str(ex)


# ═══════════════════════════════════════════════════════════
# 5) Enums
# ═══════════════════════════════════════════════════════════

class TestEnums:
    def test_project_statuses(self):
        assert len(ProjectStatus) == 7
        assert ProjectStatus.DRAFT.value == "draft"

    def test_task_statuses(self):
        assert len(TaskStatus) == 6
        assert TaskStatus.NOT_STARTED.value == "not_started"
        assert TaskStatus.READY.value == "ready"

    def test_dependency_types(self):
        assert len(DependencyType) == 4
        assert DependencyType.FS.value == "FS"

    def test_resource_types(self):
        assert len(ResourceType) == 3

    def test_risk_probabilities(self):
        assert len(RiskProbability) == 5

    def test_member_roles(self):
        assert len(MemberRole) == 4
