"""
PPM Application — Program Service

سرویس لایه کاربرد مدیریت برنامه.
"""

import logging
from typing import Optional, List
from uuid import UUID

from apps.core.event_bus.events import event_bus

from ...domain.entities.program import Program, ProgramProject
from ...domain.events.ppm_events import (
    ProgramCreated, ProgramUpdated, ProgramCompleted,
    ProgramProjectAdded, ProgramProjectRemoved,
)
from ...infrastructure.repositories import (
    DjangoProgramRepository,
    DjangoProgramProjectRepository,
)
from ..dtos.ppm_dtos import (
    ProgramCreateDTO, ProgramResponseDTO,
    ProgramProjectCreateDTO, ProgramProjectResponseDTO,
)


logger = logging.getLogger(__name__)


def _publish_event(event) -> None:
    """Publish a domain event via the platform event bus (fire-and-forget)."""
    try:
        event_bus.publish(event)
    except Exception as exc:
        logger.warning("event_publish_failed: %s — %s", type(event).__name__, exc)


class ProgramService:
    """سرویس مدیریت برنامه."""

    def __init__(self, tenant_id: UUID):
        self._tenant_id = tenant_id
        self._repo = DjangoProgramRepository()
        self._pp_repo = DjangoProgramProjectRepository()

    # ─── Program CRUD ───

    def create(self, dto: ProgramCreateDTO) -> ProgramResponseDTO:
        entity = Program(
            tenant_id=self._tenant_id,
            code=dto.code,
            name=dto.name,
            description=dto.description,
            portfolio_id=dto.portfolio_id,
            manager_id=dto.manager_id,
            objectives=dto.objectives,
            expected_benefits=dto.expected_benefits,
            budget=dto.budget,
            start_date=dto.start_date,
            end_date=dto.end_date,
            risk_level=dto.risk_level,
            tags=dto.tags or [],
            metadata=dto.metadata or {},
        )
        saved = self._repo.save(entity)
        _publish_event(ProgramCreated(
            tenant_id=self._tenant_id,
            program_id=saved.id,
            code=saved.code,
            name=saved.name,
            portfolio_id=saved.portfolio_id,
        ))
        return self._to_response(saved)

    def update(self, program_id: UUID, **kwargs) -> Optional[ProgramResponseDTO]:
        entity = self._repo.get_by_id(program_id)
        if not entity:
            return None
        for key, value in kwargs.items():
            if hasattr(entity, key):
                setattr(entity, key, value)
        saved = self._repo.save(entity)
        _publish_event(ProgramUpdated(
            tenant_id=self._tenant_id,
            program_id=saved.id,
        ))
        return self._to_response(saved)

    def complete(self, program_id: UUID) -> Optional[ProgramResponseDTO]:
        entity = self._repo.get_by_id(program_id)
        if not entity:
            return None
        entity.complete()
        saved = self._repo.save(entity)
        _publish_event(ProgramCompleted(
            tenant_id=self._tenant_id,
            program_id=saved.id,
        ))
        return self._to_response(saved)

    def get_by_id(self, program_id: UUID) -> Optional[ProgramResponseDTO]:
        entity = self._repo.get_by_id(program_id)
        if not entity:
            return None
        return self._to_response(entity)

    def list_all(self, **filters) -> List[ProgramResponseDTO]:
        entities = self._repo.find(**filters)
        return [self._to_response(e) for e in entities]

    def delete(self, program_id: UUID) -> None:
        self._repo.delete(program_id)

    # ─── Program-Project Management ───

    def add_project(self, dto: ProgramProjectCreateDTO) -> ProgramProjectResponseDTO:
        entity = ProgramProject(
            tenant_id=self._tenant_id,
            program_id=dto.program_id,
            project_id=dto.project_id,
            role=dto.role,
            priority=dto.priority,
            notes=dto.notes,
        )
        saved = self._pp_repo.save(entity)
        _publish_event(ProgramProjectAdded(
            tenant_id=self._tenant_id,
            program_id=saved.program_id,
            project_id=saved.project_id,
            role=saved.role.value if hasattr(saved.role, "value") else saved.role,
        ))
        return self._to_pp_response(saved)

    def remove_project(self, program_project_id: UUID) -> None:
        entity = self._pp_repo.get_by_id(program_project_id)
        if entity:
            _publish_event(ProgramProjectRemoved(
                tenant_id=self._tenant_id,
                program_id=entity.program_id,
                project_id=entity.project_id,
            ))
            self._pp_repo.delete(program_project_id)

    def list_program_projects(self, program_id: UUID) -> List[ProgramProjectResponseDTO]:
        entities = self._pp_repo.find_by_program(program_id)
        return [self._to_pp_response(e) for e in entities]

    # ─── Mappers ───

    @staticmethod
    def _to_response(entity: Program) -> ProgramResponseDTO:
        return ProgramResponseDTO(
            id=entity.id,
            code=entity.code,
            name=entity.name,
            description=entity.description,
            portfolio_id=entity.portfolio_id,
            manager_id=entity.manager_id,
            status=entity.status.value if hasattr(entity.status, "value") else entity.status,
            objectives=entity.objectives,
            expected_benefits=entity.expected_benefits,
            budget=entity.budget,
            start_date=entity.start_date,
            end_date=entity.end_date,
            risk_level=entity.risk_level,
            progress=entity.progress,
            tags=entity.tags or [],
            metadata=entity.metadata or {},
        )

    @staticmethod
    def _to_pp_response(entity: ProgramProject) -> ProgramProjectResponseDTO:
        return ProgramProjectResponseDTO(
            id=entity.id,
            program_id=entity.program_id,
            project_id=entity.project_id,
            role=entity.role.value if hasattr(entity.role, "value") else entity.role,
            priority=entity.priority,
            notes=entity.notes,
        )
