"""
PPM Application — Portfolio Service

سرویس‌های لایه کاربرد ماژول مدیریت پرتفولیو و برنامه.
"""

import logging
from typing import Optional, List
from uuid import UUID

from apps.core.event_bus.events import event_bus

from ...domain.entities.portfolio import Portfolio
from ...domain.events.ppm_events import PortfolioCreated, PortfolioUpdated, PortfolioClosed
from ...infrastructure.repositories import DjangoPortfolioRepository
from ..dtos.ppm_dtos import PortfolioCreateDTO, PortfolioResponseDTO


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 PortfolioService:
    """سرویس مدیریت پرتفولیو."""

    def __init__(self, tenant_id: UUID):
        self._tenant_id = tenant_id
        self._repo = DjangoPortfolioRepository()

    def create(self, dto: PortfolioCreateDTO) -> PortfolioResponseDTO:
        entity = Portfolio(
            tenant_id=self._tenant_id,
            code=dto.code,
            name=dto.name,
            description=dto.description,
            parent_id=dto.parent_id,
            risk_tolerance=dto.risk_tolerance,
            total_budget=dto.total_budget,
            start_date=dto.start_date,
            end_date=dto.end_date,
            review_cycle=dto.review_cycle,
            owner_id=dto.owner_id,
            company_id=dto.company_id,
            org_unit_id=dto.org_unit_id,
            tags=dto.tags or [],
            metadata=dto.metadata or {},
        )
        saved = self._repo.save(entity)
        _publish_event(PortfolioCreated(
            tenant_id=self._tenant_id,
            portfolio_id=saved.id,
            code=saved.code,
            name=saved.name,
        ))
        return self._to_response(saved)

    def update(self, portfolio_id: UUID, **kwargs) -> Optional[PortfolioResponseDTO]:
        entity = self._repo.get_by_id(portfolio_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(PortfolioUpdated(
            tenant_id=self._tenant_id,
            portfolio_id=saved.id,
        ))
        return self._to_response(saved)

    def close(self, portfolio_id: UUID) -> Optional[PortfolioResponseDTO]:
        entity = self._repo.get_by_id(portfolio_id)
        if not entity:
            return None
        entity.close()
        saved = self._repo.save(entity)
        _publish_event(PortfolioClosed(
            tenant_id=self._tenant_id,
            portfolio_id=saved.id,
        ))
        return self._to_response(saved)

    def get_by_id(self, portfolio_id: UUID) -> Optional[PortfolioResponseDTO]:
        entity = self._repo.get_by_id(portfolio_id)
        if not entity:
            return None
        return self._to_response(entity)

    def list_all(self, **filters) -> List[PortfolioResponseDTO]:
        entities = self._repo.find(**filters)
        return [self._to_response(e) for e in entities]

    def delete(self, portfolio_id: UUID) -> None:
        self._repo.delete(portfolio_id)

    @staticmethod
    def _to_response(entity: Portfolio) -> PortfolioResponseDTO:
        return PortfolioResponseDTO(
            id=entity.id,
            code=entity.code,
            name=entity.name,
            description=entity.description,
            parent_id=entity.parent_id,
            status=entity.status.value if hasattr(entity.status, 'value') else entity.status,
            strategic_alignment_score=entity.strategic_alignment_score,
            risk_tolerance=entity.risk_tolerance.value if hasattr(entity.risk_tolerance, 'value') else entity.risk_tolerance,
            total_budget=entity.total_budget,
            allocated_budget=entity.allocated_budget,
            start_date=entity.start_date,
            end_date=entity.end_date,
            review_cycle=entity.review_cycle.value if hasattr(entity.review_cycle, 'value') else entity.review_cycle,
            owner_id=entity.owner_id,
            company_id=entity.company_id,
            org_unit_id=entity.org_unit_id,
            tags=entity.tags or [],
            metadata=entity.metadata or {},
        )
