"""
PPM Domain — Program & ProgramProject Entities

برنامه — مجموعه‌ای هماهنگ از پروژه‌ها در چارچوب یک پرتفولیو.
ProgramProject — ارتباط M2M بین برنامه و پروژه (UUID ref).
"""

from dataclasses import dataclass, field
from datetime import date
from decimal import Decimal
from typing import Optional
from uuid import UUID

from shared.base_classes.entity import TenantEntity

from ..value_objects.common import ProgramStatus, ProgramProjectRole


@dataclass
class Program(TenantEntity):
    """
    برنامه — Aggregate Root.

    گروهی از پروژه‌های مرتبط که به صورت هماهنگ مدیریت می‌شوند
    تا منافعی بیشتر از مدیریت تکی آنها حاصل شود.
    """

    # --- Identity ---
    code: str = ""
    name: str = ""
    description: str = ""

    # --- Hierarchy ---
    portfolio_id: Optional[UUID] = None  # FK → Portfolio

    # --- People ---
    manager_id: Optional[UUID] = None    # → User (مدیر برنامه)

    # --- Status ---
    status: ProgramStatus = ProgramStatus.DRAFT

    # --- Strategy ---
    objectives: str = ""                 # اهداف برنامه
    expected_benefits: str = ""          # منافع مورد انتظار

    # --- Financial ---
    budget: Decimal = Decimal("0")

    # --- Timeline ---
    start_date: Optional[date] = None
    end_date: Optional[date] = None

    # --- Progress ---
    risk_level: str = "medium"           # low / medium / high / critical
    progress: Decimal = Decimal("0")     # 0-100 درصد پیشرفت

    # --- Metadata ---
    tags: Optional[list] = field(default_factory=list)
    metadata: Optional[dict] = field(default_factory=dict)

    # ─── Business Rules ───

    def can_activate(self) -> bool:
        return self.status == ProgramStatus.DRAFT and self.name != ""

    def can_complete(self) -> bool:
        return self.status == ProgramStatus.ACTIVE

    def activate(self) -> None:
        if not self.can_activate():
            raise ValueError("برنامه قابل فعال‌سازی نیست.")
        self.status = ProgramStatus.ACTIVE

    def complete(self) -> None:
        if not self.can_complete():
            raise ValueError("برنامه قابل تکمیل نیست.")
        self.status = ProgramStatus.COMPLETED

    def hold(self) -> None:
        if self.status != ProgramStatus.ACTIVE:
            raise ValueError("فقط برنامه فعال قابل توقف است.")
        self.status = ProgramStatus.ON_HOLD

    def close(self) -> None:
        if self.status not in (ProgramStatus.COMPLETED, ProgramStatus.CANCELLED):
            raise ValueError("فقط برنامه تکمیل‌شده یا لغوشده قابل بسته شدن است.")
        self.status = ProgramStatus.CLOSED

    def cancel(self) -> None:
        if self.status in (ProgramStatus.CLOSED,):
            raise ValueError("برنامه بسته‌شده قابل لغو نیست.")
        self.status = ProgramStatus.CANCELLED


@dataclass
class ProgramProject(TenantEntity):
    """
    ارتباط M2M بین برنامه و پروژه.

    از UUID ref برای پروژه استفاده می‌شود (cross-module).
    """

    program_id: Optional[UUID] = None    # FK → Program
    project_id: Optional[UUID] = None    # UUID ref → PM.Project
    role: ProgramProjectRole = ProgramProjectRole.CORE
    priority: int = 0
    notes: str = ""
