"""
PM Module — Domain Value Objects

Value objects representing immutable concepts in the PM domain.
"""

from dataclasses import dataclass
from datetime import date, datetime, timedelta
from decimal import Decimal
from enum import Enum
from typing import Optional
from shared.base_classes.value_object import BaseValueObject


# ─── Enums ───────────────────────────────────────────────────────────

class ProjectStatus(str, Enum):
    """وضعیت پروژه"""
    DRAFT = "draft"
    PLANNING = "planning"
    ACTIVE = "active"
    ON_HOLD = "on_hold"
    COMPLETED = "completed"
    CANCELLED = "cancelled"
    ARCHIVED = "archived"


class ProjectPriority(str, Enum):
    """اولویت پروژه"""
    CRITICAL = "critical"
    HIGH = "high"
    MEDIUM = "medium"
    LOW = "low"


class ProjectType(str, Enum):
    """نوع پروژه"""
    PORTFOLIO = "portfolio"
    PROGRAM = "program"
    PROJECT = "project"
    SUB_PROJECT = "sub_project"


class TaskStatus(str, Enum):
    """وضعیت تسک"""
    NOT_STARTED = "not_started"
    READY = "ready"
    IN_PROGRESS = "in_progress"
    COMPLETED = "completed"
    ON_HOLD = "on_hold"
    CANCELLED = "cancelled"


class TaskType(str, Enum):
    """نوع تسک"""
    TASK = "task"
    MILESTONE = "milestone"
    SUMMARY = "summary"


class DependencyType(str, Enum):
    """نوع وابستگی"""
    FS = "FS"  # Finish-to-Start
    SS = "SS"  # Start-to-Start
    FF = "FF"  # Finish-to-Finish
    SF = "SF"  # Start-to-Finish


class ResourceType(str, Enum):
    """نوع منبع"""
    HUMAN = "human"
    EQUIPMENT = "equipment"
    MATERIAL = "material"


class CostType(str, Enum):
    """نوع هزینه"""
    FIXED = "fixed"
    VARIABLE = "variable"
    OVERHEAD = "overhead"


class RiskProbability(str, Enum):
    """احتمال ریسک"""
    VERY_LOW = "very_low"
    LOW = "low"
    MEDIUM = "medium"
    HIGH = "high"
    VERY_HIGH = "very_high"


class RiskImpact(str, Enum):
    """تأثیر ریسک"""
    NEGLIGIBLE = "negligible"
    MINOR = "minor"
    MODERATE = "moderate"
    MAJOR = "major"
    CRITICAL = "critical"


class RiskStatus(str, Enum):
    """وضعیت ریسک"""
    IDENTIFIED = "identified"
    ANALYZING = "analyzing"
    PLANNED = "planned"
    MITIGATING = "mitigating"
    MONITORING = "monitoring"
    CLOSED = "closed"
    OCCURRED = "occurred"


class RiskResponseStrategy(str, Enum):
    """استراتژی پاسخ ریسک"""
    AVOID = "avoid"
    MITIGATE = "mitigate"
    TRANSFER = "transfer"
    ACCEPT = "accept"
    EXPLOIT = "exploit"
    ENHANCE = "enhance"
    SHARE = "share"


class IssueCategory(str, Enum):
    """دسته‌بندی مسئله"""
    TECHNICAL = "technical"
    RESOURCE = "resource"
    SCHEDULE = "schedule"
    COST = "cost"
    SCOPE = "scope"
    QUALITY = "quality"
    COMMUNICATION = "communication"
    OTHER = "other"


class IssueSeverity(str, Enum):
    """شدت مسئله"""
    CRITICAL = "critical"
    MAJOR = "major"
    MODERATE = "moderate"
    MINOR = "minor"


class IssuePriority(str, Enum):
    """اولویت مسئله"""
    CRITICAL = "critical"
    HIGH = "high"
    MEDIUM = "medium"
    LOW = "low"


class IssueStatus(str, Enum):
    """وضعیت مسئله"""
    OPEN = "open"
    IN_PROGRESS = "in_progress"
    ON_HOLD = "on_hold"
    RESOLVED = "resolved"
    CLOSED = "closed"


class OBSNodeType(str, Enum):
    """نوع گره ساختار شکست سازمانی"""
    DEPARTMENT = "department"      # دپارتمان
    DIVISION = "division"          # بخش
    TEAM = "team"                  # تیم
    INDIVIDUAL = "individual"      # فرد
    EXTERNAL = "external"          # پیمانکار/خارجی


class RBSNodeType(str, Enum):
    """نوع گره ساختار شکست منابع"""
    CATEGORY = "category"          # دسته‌بندی
    HUMAN = "human"                # نیروی انسانی
    EQUIPMENT = "equipment"        # تجهیزات
    MATERIAL = "material"          # مواد
    FACILITY = "facility"          # تأسیسات
    FINANCIAL = "financial"        # مالی
    INFORMATION = "information"    # اطلاعاتی


# ─── Quality Management Enums ────────────────────────────────────────

class InspectionType(str, Enum):
    """نوع بازرسی"""
    QUALITY_AUDIT = "quality_audit"        # ممیزی کیفیت
    PROCESS_INSPECTION = "process_inspection"  # بازرسی فرآیند
    PRODUCT_INSPECTION = "product_inspection"  # بازرسی محصول
    MILESTONE_REVIEW = "milestone_review"      # بازبینی نقطه عطف
    FINAL_INSPECTION = "final_inspection"      # بازرسی نهایی
    RANDOM_SAMPLING = "random_sampling"        # نمونه‌برداری تصادفی


class InspectionStatus(str, Enum):
    """وضعیت بازرسی"""
    PLANNED = "planned"            # برنامه‌ریزی‌شده
    IN_PROGRESS = "in_progress"    # در حال انجام
    COMPLETED = "completed"        # تکمیل‌شده
    CANCELLED = "cancelled"        # لغو‌شده


class InspectionResult(str, Enum):
    """نتیجه بازرسی"""
    PASSED = "passed"              # قبول
    FAILED = "failed"              # مردود
    CONDITIONAL = "conditional"    # مشروط
    NOT_APPLICABLE = "na"          # غیرقابل‌اعمال


class NCRSeverity(str, Enum):
    """شدت عدم انطباق"""
    MINOR = "minor"                # جزئی
    MAJOR = "major"                # عمده
    CRITICAL = "critical"          # بحرانی


class NCRStatus(str, Enum):
    """وضعیت عدم انطباق"""
    OPEN = "open"                  # باز
    UNDER_REVIEW = "under_review"  # در حال بررسی
    ACTION_REQUIRED = "action_required"  # نیاز به اقدام
    IN_PROGRESS = "in_progress"    # در حال رفع
    RESOLVED = "resolved"          # رفع‌شده
    VERIFIED = "verified"          # تأیید شده
    CLOSED = "closed"              # بسته


class QualityPlanStatus(str, Enum):
    """وضعیت طرح کیفیت"""
    DRAFT = "draft"                # پیش‌نویس
    ACTIVE = "active"              # فعال
    UNDER_REVIEW = "under_review"  # در حال بازبینی
    APPROVED = "approved"          # تأیید‌شده
    ARCHIVED = "archived"          # بایگانی


class ChangeRequestStatus(str, Enum):
    """وضعیت درخواست تغییر"""
    DRAFT = "draft"
    SUBMITTED = "submitted"
    UNDER_REVIEW = "under_review"
    APPROVED = "approved"
    REJECTED = "rejected"
    IMPLEMENTED = "implemented"
    CLOSED = "closed"


class ChangeRequestPriority(str, Enum):
    """اولویت درخواست تغییر"""
    CRITICAL = "critical"
    HIGH = "high"
    MEDIUM = "medium"
    LOW = "low"


class TimesheetStatus(str, Enum):
    """وضعیت تایم‌شیت"""
    DRAFT = "draft"
    SUBMITTED = "submitted"
    APPROVED = "approved"
    REJECTED = "rejected"


class BaselineType(str, Enum):
    """نوع خط پایه"""
    INITIAL = "initial"
    REVISED = "revised"
    CURRENT = "current"


class MemberRole(str, Enum):
    """نقش عضو در پروژه"""
    MANAGER = "manager"
    MEMBER = "member"
    VIEWER = "viewer"
    STAKEHOLDER = "stakeholder"


class AssignmentPolicy(str, Enum):
    """سیاست تخصیص تسک"""
    MANUAL = "manual"                   # تخصیص دستی توسط مدیر
    AUTO_LEAST_LOAD = "auto_least_load"  # تخصیص خودکار به منبع با کمترین بار
    AUTO_ROUND_ROBIN = "auto_round_robin" # تخصیص چرخشی
    SELF_CLAIM = "self_claim"            # انتخاب توسط خود اعضا


class TaskAssignmentRole(str, Enum):
    """نقش تخصیص‌یافته در تسک"""
    PRIMARY = "primary"       # مسئول اصلی
    SECONDARY = "secondary"   # مسئول فرعی
    REVIEWER = "reviewer"     # بازبین
    OBSERVER = "observer"     # ناظر


class RACIRole(str, Enum):
    """نقش RACI"""
    RESPONSIBLE = "R"    # مسئول
    ACCOUNTABLE = "A"    # پاسخگو
    CONSULTED = "C"      # مشاوره
    INFORMED = "I"       # مطلع


class TaskConstraintType(str, Enum):
    """نوع محدودیت تسک (Primavera-style)"""
    ASAP = "asap"                           # در اولین فرصت — پیش‌فرض
    ALAP = "alap"                           # در آخرین فرصت
    MUST_START_ON = "must_start_on"         # باید در تاریخ مشخص شروع شود
    MUST_FINISH_ON = "must_finish_on"       # باید در تاریخ مشخص تمام شود
    START_NO_EARLIER_THAN = "start_no_earlier_than"  # شروع نه زودتر از
    START_NO_LATER_THAN = "start_no_later_than"      # شروع نه دیرتر از
    FINISH_NO_EARLIER_THAN = "finish_no_earlier_than" # پایان نه زودتر از
    FINISH_NO_LATER_THAN = "finish_no_later_than"     # پایان نه دیرتر از


class SchedulingMode(str, Enum):
    """حالت زمان‌بندی تسک"""
    AUTO = "auto"       # محاسبه خودکار توسط CPM
    MANUAL = "manual"   # تاریخ‌ها دستی تنظیم شود


class DurationType(str, Enum):
    """نوع مدت زمان"""
    FIXED_DURATION = "fixed_duration"   # مدت ثابت
    FIXED_UNITS = "fixed_units"         # واحد منبع ثابت
    FIXED_WORK = "fixed_work"           # کار ثابت


class PercentCompleteType(str, Enum):
    """نوع محاسبه درصد پیشرفت"""
    PHYSICAL = "physical"   # فیزیکی — دستی
    DURATION = "duration"   # بر اساس مدت زمان سپری‌شده
    WORK = "work"           # بر اساس ساعت کار انجام‌شده


# ─── Value Objects ───────────────────────────────────────────────────

@dataclass(frozen=True)
class DateRange(BaseValueObject):
    """بازه زمانی"""
    start_date: date
    end_date: date

    def __post_init__(self):
        if self.start_date > self.end_date:
            raise ValueError("تاریخ شروع نمی‌تواند بعد از تاریخ پایان باشد")

    @property
    def duration_days(self) -> int:
        return (self.end_date - self.start_date).days + 1

    @property
    def duration_business_days(self) -> int:
        """روزهای کاری (بدون جمعه)"""
        count = 0
        current = self.start_date
        while current <= self.end_date:
            if current.weekday() != 4:  # 4 = Friday
                count += 1
            current += timedelta(days=1)
        return count

    def overlaps(self, other: "DateRange") -> bool:
        return self.start_date <= other.end_date and other.start_date <= self.end_date

    def contains(self, d: date) -> bool:
        return self.start_date <= d <= self.end_date


@dataclass(frozen=True)
class WBSCode(BaseValueObject):
    """کد WBS مانند 1.2.3.1"""
    code: str

    def __post_init__(self):
        if not self.code:
            raise ValueError("کد WBS نمی‌تواند خالی باشد")
        parts = self.code.split('.')
        for part in parts:
            if not part.isdigit():
                raise ValueError(f"بخش‌های کد WBS باید عدد باشند: {self.code}")

    @property
    def level(self) -> int:
        return len(self.code.split('.'))

    @property
    def parent_code(self) -> Optional[str]:
        parts = self.code.split('.')
        if len(parts) <= 1:
            return None
        return '.'.join(parts[:-1])

    def is_child_of(self, other: "WBSCode") -> bool:
        return self.code.startswith(other.code + '.')


@dataclass(frozen=True)
class Percentage(BaseValueObject):
    """درصد — 0 تا 100"""
    value: int

    def __post_init__(self):
        if not (0 <= self.value <= 100):
            raise ValueError(f"درصد باید بین ۰ تا ۱۰۰ باشد: {self.value}")


@dataclass(frozen=True)
class Duration(BaseValueObject):
    """مدت زمان بر حسب روز"""
    days: int

    def __post_init__(self):
        if self.days < 0:
            raise ValueError("مدت زمان نمی‌تواند منفی باشد")

    def to_hours(self, hours_per_day: int = 8) -> int:
        return self.days * hours_per_day


@dataclass(frozen=True)
class CostRate(BaseValueObject):
    """نرخ هزینه منبع"""
    standard_rate: Decimal
    overtime_rate: Decimal
    currency: str = "IRR"

    def __post_init__(self):
        if self.standard_rate < 0:
            raise ValueError("نرخ استاندارد نمی‌تواند منفی باشد")
        if self.overtime_rate < 0:
            raise ValueError("نرخ اضافه‌کاری نمی‌تواند منفی باشد")


@dataclass(frozen=True)
class EVMMetrics(BaseValueObject):
    """شاخص‌های مدیریت ارزش کسب‌شده (EVM)"""
    planned_value: Decimal       # PV — ارزش برنامه‌ریزی‌شده
    earned_value: Decimal        # EV — ارزش کسب‌شده
    actual_cost: Decimal         # AC — هزینه واقعی
    budget_at_completion: Decimal  # BAC — بودجه در تکمیل

    @property
    def cost_variance(self) -> Decimal:
        """CV = EV - AC"""
        return self.earned_value - self.actual_cost

    @property
    def schedule_variance(self) -> Decimal:
        """SV = EV - PV"""
        return self.earned_value - self.planned_value

    @property
    def cost_performance_index(self) -> Optional[Decimal]:
        """CPI = EV / AC"""
        if self.actual_cost == 0:
            return None
        return self.earned_value / self.actual_cost

    @property
    def schedule_performance_index(self) -> Optional[Decimal]:
        """SPI = EV / PV"""
        if self.planned_value == 0:
            return None
        return self.earned_value / self.planned_value

    @property
    def estimate_at_completion(self) -> Optional[Decimal]:
        """EAC = BAC / CPI"""
        cpi = self.cost_performance_index
        if cpi is None or cpi == 0:
            return None
        return self.budget_at_completion / cpi

    @property
    def estimate_to_complete(self) -> Optional[Decimal]:
        """ETC = EAC - AC"""
        eac = self.estimate_at_completion
        if eac is None:
            return None
        return eac - self.actual_cost

    @property
    def variance_at_completion(self) -> Optional[Decimal]:
        """VAC = BAC - EAC"""
        eac = self.estimate_at_completion
        if eac is None:
            return None
        return self.budget_at_completion - eac

    @property
    def to_complete_performance_index(self) -> Optional[Decimal]:
        """TCPI = (BAC - EV) / (BAC - AC)"""
        denominator = self.budget_at_completion - self.actual_cost
        if denominator == 0:
            return None
        return (self.budget_at_completion - self.earned_value) / denominator


@dataclass(frozen=True)
class RiskScore(BaseValueObject):
    """امتیاز ریسک = احتمال × تأثیر"""
    probability: RiskProbability
    impact: RiskImpact

    _PROBABILITY_WEIGHT = {
        RiskProbability.VERY_LOW: 1,
        RiskProbability.LOW: 2,
        RiskProbability.MEDIUM: 3,
        RiskProbability.HIGH: 4,
        RiskProbability.VERY_HIGH: 5,
    }

    _IMPACT_WEIGHT = {
        RiskImpact.NEGLIGIBLE: 1,
        RiskImpact.MINOR: 2,
        RiskImpact.MODERATE: 3,
        RiskImpact.MAJOR: 4,
        RiskImpact.CRITICAL: 5,
    }

    @property
    def score(self) -> int:
        return self._PROBABILITY_WEIGHT[self.probability] * self._IMPACT_WEIGHT[self.impact]

    @property
    def level(self) -> str:
        s = self.score
        if s >= 16:
            return "critical"
        elif s >= 9:
            return "high"
        elif s >= 4:
            return "medium"
        return "low"
