"""
PPM Application — Business Case Service

سرویس لایه کاربرد مدیریت کیس تجاری + ارزیابی مالی.
"""

import logging
from decimal import Decimal
from typing import Optional, List
from uuid import UUID

from apps.core.event_bus.events import event_bus

from ...domain.entities.business_case import BusinessCase, CashFlowProjection
from ...domain.events.ppm_events import (
    BusinessCaseCreated, BusinessCaseSubmitted,
    BusinessCaseApproved, BusinessCaseRejected,
)
from ...domain.services.financial_evaluator import FinancialEvaluator
from ...infrastructure.repositories import (
    DjangoBusinessCaseRepository,
    DjangoCashFlowProjectionRepository,
)
from ..dtos.ppm_dtos import (
    BusinessCaseCreateDTO, BusinessCaseResponseDTO,
    CashFlowCreateDTO, CashFlowResponseDTO,
    FinancialEvaluationDTO,
)


logger = logging.getLogger(__name__)


def _publish_event(event) -> None:
    try:
        event_bus.publish(event)
    except Exception as exc:
        logger.warning("event_publish_failed: %s — %s", type(event).__name__, exc)


class BusinessCaseService:
    """سرویس مدیریت کیس تجاری."""

    def __init__(self, tenant_id: UUID):
        self._tenant_id = tenant_id
        self._bc_repo = DjangoBusinessCaseRepository()
        self._cf_repo = DjangoCashFlowProjectionRepository()
        self._evaluator = FinancialEvaluator()

    # ─── CRUD ───

    def create(self, dto: BusinessCaseCreateDTO) -> BusinessCaseResponseDTO:
        entity = BusinessCase(
            tenant_id=self._tenant_id,
            title=dto.title,
            description=dto.description,
            program_id=dto.program_id,
            project_id=dto.project_id,
            sponsor_id=dto.sponsor_id,
            prepared_by_id=dto.prepared_by_id,
            strategic_fit_score=dto.strategic_fit_score,
            total_investment=dto.total_investment,
            expected_revenue=dto.expected_revenue,
            expected_savings=dto.expected_savings,
            payback_period_months=dto.payback_period_months,
            assumptions=dto.assumptions,
            constraints=dto.constraints,
            risks_summary=dto.risks_summary,
            metadata=dto.metadata or {},
        )
        saved = self._bc_repo.save(entity)
        _publish_event(BusinessCaseCreated(
            tenant_id=self._tenant_id,
            business_case_id=saved.id,
            title=saved.title,
        ))
        return self._to_response(saved)

    def update(self, bc_id: UUID, **kwargs) -> Optional[BusinessCaseResponseDTO]:
        entity = self._bc_repo.get_by_id(bc_id)
        if not entity:
            return None
        for key, value in kwargs.items():
            if hasattr(entity, key):
                setattr(entity, key, value)
        saved = self._bc_repo.save(entity)
        return self._to_response(saved)

    def get_by_id(self, bc_id: UUID) -> Optional[BusinessCaseResponseDTO]:
        entity = self._bc_repo.get_by_id(bc_id)
        if not entity:
            return None
        return self._to_response(entity)

    def list_all(self, **filters) -> List[BusinessCaseResponseDTO]:
        entities = self._bc_repo.find(**filters)
        return [self._to_response(e) for e in entities]

    def delete(self, bc_id: UUID) -> None:
        self._bc_repo.delete(bc_id)

    # ─── Status transitions ───

    def submit(self, bc_id: UUID) -> Optional[BusinessCaseResponseDTO]:
        entity = self._bc_repo.get_by_id(bc_id)
        if not entity:
            return None
        entity.submit()
        saved = self._bc_repo.save(entity)
        _publish_event(BusinessCaseSubmitted(
            tenant_id=self._tenant_id,
            business_case_id=saved.id,
        ))
        return self._to_response(saved)

    def approve(self, bc_id: UUID) -> Optional[BusinessCaseResponseDTO]:
        entity = self._bc_repo.get_by_id(bc_id)
        if not entity:
            return None
        entity.approve()
        saved = self._bc_repo.save(entity)
        _publish_event(BusinessCaseApproved(
            tenant_id=self._tenant_id,
            business_case_id=saved.id,
        ))
        return self._to_response(saved)

    def reject(self, bc_id: UUID) -> Optional[BusinessCaseResponseDTO]:
        entity = self._bc_repo.get_by_id(bc_id)
        if not entity:
            return None
        entity.reject()
        saved = self._bc_repo.save(entity)
        _publish_event(BusinessCaseRejected(
            tenant_id=self._tenant_id,
            business_case_id=saved.id,
        ))
        return self._to_response(saved)

    # ─── CashFlow ───

    def add_cash_flow(self, dto: CashFlowCreateDTO) -> CashFlowResponseDTO:
        entity = CashFlowProjection(
            tenant_id=self._tenant_id,
            business_case_id=dto.business_case_id,
            year=dto.year,
            investment=dto.investment,
            revenue=dto.revenue,
            savings=dto.savings,
            operational_cost=dto.operational_cost,
        )
        saved = self._cf_repo.save(entity)
        return self._to_cf_response(saved)

    def list_cash_flows(self, bc_id: UUID) -> List[CashFlowResponseDTO]:
        entities = self._cf_repo.find_by_business_case(bc_id)
        return [self._to_cf_response(e) for e in entities]

    def delete_cash_flow(self, cf_id: UUID) -> None:
        self._cf_repo.delete(cf_id)

    # ─── Financial Evaluation ───

    def evaluate(self, bc_id: UUID, discount_rate: Decimal = Decimal("0.10")) -> Optional[FinancialEvaluationDTO]:
        entity = self._bc_repo.get_by_id(bc_id)
        if not entity:
            return None

        cash_flows = self._cf_repo.find_by_business_case(bc_id)
        if not cash_flows:
            return FinancialEvaluationDTO(
                roi=entity.roi,
            )

        return FinancialEvaluationDTO(
            npv=self._evaluator.calculate_npv(cash_flows, discount_rate),
            irr=self._evaluator.calculate_irr(cash_flows),
            payback_period=self._evaluator.calculate_payback_period(cash_flows),
            roi=self._evaluator.calculate_roi(entity.total_investment, entity.net_benefit),
            benefit_cost_ratio=self._evaluator.calculate_benefit_cost_ratio(cash_flows, discount_rate),
        )

    # ─── Mappers ───

    @staticmethod
    def _to_response(entity: BusinessCase) -> BusinessCaseResponseDTO:
        return BusinessCaseResponseDTO(
            id=entity.id,
            title=entity.title,
            description=entity.description,
            program_id=entity.program_id,
            project_id=entity.project_id,
            status=entity.status.value if hasattr(entity.status, "value") else entity.status,
            sponsor_id=entity.sponsor_id,
            prepared_by_id=entity.prepared_by_id,
            strategic_fit_score=entity.strategic_fit_score,
            total_investment=entity.total_investment,
            expected_revenue=entity.expected_revenue,
            expected_savings=entity.expected_savings,
            payback_period_months=entity.payback_period_months,
            assumptions=entity.assumptions,
            constraints=entity.constraints,
            risks_summary=entity.risks_summary,
            metadata=entity.metadata or {},
        )

    @staticmethod
    def _to_cf_response(entity: CashFlowProjection) -> CashFlowResponseDTO:
        return CashFlowResponseDTO(
            id=entity.id,
            business_case_id=entity.business_case_id,
            year=entity.year,
            investment=entity.investment,
            revenue=entity.revenue,
            savings=entity.savings,
            operational_cost=entity.operational_cost,
            net_cash_flow=entity.net_cash_flow,
        )
