"""
PPM Application — Benefit Realization Service

سرویس لایه اپلیکیشن برای مدیریت برنامه تحقق منافع.
"""

import logging
from decimal import Decimal
from typing import Optional
from uuid import UUID

from apps.core.event_bus.events import event_bus
from ...domain.entities.benefit import BenefitPlan, Benefit, BenefitMeasurement
from ...domain.events.ppm_events import (
    BenefitPlanCreated,
    BenefitCreated,
    BenefitRealized,
    BenefitMeasured,
)
from ...domain.value_objects.common import BenefitStatus, BenefitType, MeasurementFrequency
from ...infrastructure.repositories.benefit_plan_repo import DjangoBenefitPlanRepository
from ...infrastructure.repositories.benefit_repo import DjangoBenefitRepository
from ...infrastructure.repositories.benefit_measurement_repo import DjangoBenefitMeasurementRepository

logger = logging.getLogger(__name__)


class BenefitService:
    """سرویس مدیریت برنامه تحقق منافع، منافع و اندازه‌گیری‌ها."""

    def __init__(self):
        self.plan_repo = DjangoBenefitPlanRepository()
        self.benefit_repo = DjangoBenefitRepository()
        self.measurement_repo = DjangoBenefitMeasurementRepository()

    # ─── Benefit Plan CRUD ───────────────────────────────────

    def create_plan(self, dto, tenant_id: UUID) -> BenefitPlan:
        entity = BenefitPlan(
            tenant_id=tenant_id,
            name=dto.name,
            description=dto.description,
            program_id=dto.program_id,
            portfolio_id=dto.portfolio_id,
            is_active=dto.is_active,
            metadata=dto.metadata or {},
        )
        saved = self.plan_repo.save(entity)
        event_bus.publish(BenefitPlanCreated(
            tenant_id=tenant_id,
            benefit_plan_id=saved.id,
            name=saved.name,
        ))
        logger.info(f"BenefitPlan created: {saved.id}")
        return saved

    def get_plan(self, plan_id: UUID):
        return self.plan_repo.get_by_id(plan_id)

    def list_plans(self, **filters) -> list:
        return self.plan_repo.find(**filters)

    def update_plan(self, plan_id: UUID, data: dict):
        entity = self.plan_repo.get_by_id(plan_id)
        if not entity:
            return None
        for key, value in data.items():
            if hasattr(entity, key):
                setattr(entity, key, value)
        return self.plan_repo.save(entity)

    def delete_plan(self, plan_id: UUID) -> None:
        self.plan_repo.delete(plan_id)

    def activate_plan(self, plan_id: UUID):
        entity = self.plan_repo.get_by_id(plan_id)
        if not entity:
            return None
        entity.activate()
        return self.plan_repo.save(entity)

    def deactivate_plan(self, plan_id: UUID):
        entity = self.plan_repo.get_by_id(plan_id)
        if not entity:
            return None
        entity.deactivate()
        return self.plan_repo.save(entity)

    # ─── Benefit CRUD ────────────────────────────────────────

    def create_benefit(self, dto, tenant_id: UUID) -> Benefit:
        entity = Benefit(
            tenant_id=tenant_id,
            benefit_plan_id=dto.benefit_plan_id,
            title=dto.title,
            description=dto.description,
            benefit_type=BenefitType(dto.benefit_type) if isinstance(dto.benefit_type, str) else dto.benefit_type,
            category=dto.category,
            target_value=dto.target_value,
            actual_value=dto.actual_value,
            measurement_method=dto.measurement_method,
            frequency=MeasurementFrequency(dto.frequency) if isinstance(dto.frequency, str) else dto.frequency,
            owner_id=dto.owner_id,
            target_date=dto.target_date,
            metadata=dto.metadata or {},
        )
        saved = self.benefit_repo.save(entity)
        event_bus.publish(BenefitCreated(
            tenant_id=tenant_id,
            benefit_id=saved.id,
            benefit_plan_id=saved.benefit_plan_id,
            title=saved.title,
        ))
        logger.info(f"Benefit created: {saved.id}")
        return saved

    def get_benefit(self, benefit_id: UUID):
        return self.benefit_repo.get_by_id(benefit_id)

    def list_benefits(self, **filters) -> list:
        return self.benefit_repo.find(**filters)

    def list_benefits_by_plan(self, benefit_plan_id: UUID) -> list:
        return self.benefit_repo.find_by_plan(benefit_plan_id)

    def update_benefit(self, benefit_id: UUID, data: dict):
        entity = self.benefit_repo.get_by_id(benefit_id)
        if not entity:
            return None
        for key, value in data.items():
            if hasattr(entity, key):
                setattr(entity, key, value)
        return self.benefit_repo.save(entity)

    def delete_benefit(self, benefit_id: UUID) -> None:
        self.benefit_repo.delete(benefit_id)

    def change_benefit_status(self, benefit_id: UUID, action: str):
        """تغییر وضعیت منفعت: plan / realize / fail."""
        entity = self.benefit_repo.get_by_id(benefit_id)
        if not entity:
            return None
        if action == "plan":
            entity.plan()
        elif action == "realize":
            entity.realize()
            event_bus.publish(BenefitRealized(
                tenant_id=entity.tenant_id,
                benefit_id=entity.id,
            ))
        elif action == "fail":
            entity.fail()
        else:
            raise ValueError(f"Invalid action: {action}")
        return self.benefit_repo.save(entity)

    # ─── Benefit Measurement CRUD ────────────────────────────

    def add_measurement(self, dto, tenant_id: UUID) -> BenefitMeasurement:
        entity = BenefitMeasurement(
            tenant_id=tenant_id,
            benefit_id=dto.benefit_id,
            measurement_date=dto.measurement_date,
            value=dto.value,
            notes=dto.notes,
            measured_by_id=dto.measured_by_id,
        )
        saved = self.measurement_repo.save(entity)

        # Update benefit actual_value with latest measurement
        benefit = self.benefit_repo.get_by_id(dto.benefit_id)
        if benefit:
            benefit.update_actual(dto.value)
            self.benefit_repo.save(benefit)

        event_bus.publish(BenefitMeasured(
            tenant_id=tenant_id,
            benefit_id=dto.benefit_id,
            measurement_id=saved.id,
            value=str(saved.value),
        ))
        logger.info(f"BenefitMeasurement created: {saved.id}")
        return saved

    def list_measurements(self, benefit_id: UUID) -> list:
        return self.measurement_repo.find_by_benefit(benefit_id)

    def delete_measurement(self, measurement_id: UUID) -> None:
        self.measurement_repo.delete(measurement_id)
