"""
PPM Infrastructure — Mappers

مپرهای Entity ↔ ORM Model.
"""

from ...domain.entities.portfolio import Portfolio
from ...domain.entities.program import Program, ProgramProject
from ...domain.entities.business_case import BusinessCase, CashFlowProjection
from ...domain.entities.scoring import (
    ScoringModel as ScoringModelEntity,
    ScoringCriterion as ScoringCriterionEntity,
    ProjectScore as ProjectScoreEntity,
)
from ...domain.entities.benefit import (
    BenefitPlan as BenefitPlanEntity,
    Benefit as BenefitEntity,
    BenefitMeasurement as BenefitMeasurementEntity,
)
from ...domain.entities.capacity import (
    CapacityPlan as CapacityPlanEntity,
    ResourceDemand as ResourceDemandEntity,
    ResourceSupply as ResourceSupplyEntity,
)
from ...domain.entities.dependency import (
    CrossProjectDependency as CrossProjectDependencyEntity,
)
from ...domain.value_objects.common import (
    PortfolioStatus, ReviewCycle, RiskTolerance,
    ProgramStatus, ProgramProjectRole,
    BusinessCaseStatus, ScoringCriterionCategory,
    BenefitType, BenefitStatus, MeasurementFrequency,
)
from ..persistence.models import (
    PortfolioModel, ProgramModel, ProgramProjectModel,
    BusinessCaseModel, CashFlowProjectionModel,
    ScoringModelModel, ScoringCriterionModel, ProjectScoreModel,
    BenefitPlanModel, BenefitModel, BenefitMeasurementModel,
    CapacityPlanModel, ResourceDemandModel, ResourceSupplyModel,
)


class PortfolioMapper:
    """Mapper: Portfolio Entity ↔ PortfolioModel."""

    @staticmethod
    def to_entity(model: PortfolioModel) -> Portfolio:
        return Portfolio(
            id=model.id,
            tenant_id=model.tenant_id,
            created_at=model.created_at,
            updated_at=model.updated_at,
            code=model.code,
            name=model.name,
            description=model.description or "",
            parent_id=model.parent_id,
            status=PortfolioStatus(model.status),
            strategic_alignment_score=model.strategic_alignment_score,
            risk_tolerance=RiskTolerance(model.risk_tolerance),
            total_budget=model.total_budget,
            allocated_budget=model.allocated_budget,
            start_date=model.start_date,
            end_date=model.end_date,
            review_cycle=ReviewCycle(model.review_cycle),
            owner_id=model.owner_id,
            company_id=model.company_id,
            org_unit_id=model.org_unit_id,
            tags=model.tags or [],
            metadata=model.metadata or {},
        )

    @staticmethod
    def to_model(entity: Portfolio, model: PortfolioModel = None) -> PortfolioModel:
        if model is None:
            model = PortfolioModel()
        model.id = entity.id
        model.code = entity.code
        model.name = entity.name
        model.description = entity.description
        model.parent_id = entity.parent_id
        model.status = entity.status.value if isinstance(entity.status, PortfolioStatus) else entity.status
        model.strategic_alignment_score = entity.strategic_alignment_score
        model.risk_tolerance = entity.risk_tolerance.value if isinstance(entity.risk_tolerance, RiskTolerance) else entity.risk_tolerance
        model.total_budget = entity.total_budget
        model.allocated_budget = entity.allocated_budget
        model.start_date = entity.start_date
        model.end_date = entity.end_date
        model.review_cycle = entity.review_cycle.value if isinstance(entity.review_cycle, ReviewCycle) else entity.review_cycle
        model.owner_id = entity.owner_id
        model.company_id = entity.company_id
        model.org_unit_id = entity.org_unit_id
        model.tags = entity.tags
        model.metadata = entity.metadata
        return model


class ProgramMapper:
    """Mapper: Program Entity ↔ ProgramModel."""

    @staticmethod
    def to_entity(model: ProgramModel) -> Program:
        return Program(
            id=model.id,
            tenant_id=model.tenant_id,
            created_at=model.created_at,
            updated_at=model.updated_at,
            code=model.code,
            name=model.name,
            description=model.description or "",
            portfolio_id=model.portfolio_id,
            manager_id=model.manager_id,
            status=ProgramStatus(model.status),
            objectives=model.objectives or "",
            expected_benefits=model.expected_benefits or "",
            budget=model.budget,
            start_date=model.start_date,
            end_date=model.end_date,
            risk_level=model.risk_level,
            progress=model.progress,
            tags=model.tags or [],
            metadata=model.metadata or {},
        )

    @staticmethod
    def to_model(entity: Program, model: ProgramModel = None) -> ProgramModel:
        if model is None:
            model = ProgramModel()
        model.id = entity.id
        model.code = entity.code
        model.name = entity.name
        model.description = entity.description
        model.portfolio_id = entity.portfolio_id
        model.manager_id = entity.manager_id
        model.status = entity.status.value if isinstance(entity.status, ProgramStatus) else entity.status
        model.objectives = entity.objectives
        model.expected_benefits = entity.expected_benefits
        model.budget = entity.budget
        model.start_date = entity.start_date
        model.end_date = entity.end_date
        model.risk_level = entity.risk_level
        model.progress = entity.progress
        model.tags = entity.tags
        model.metadata = entity.metadata
        return model


class ProgramProjectMapper:
    """Mapper: ProgramProject Entity ↔ ProgramProjectModel."""

    @staticmethod
    def to_entity(model: ProgramProjectModel) -> ProgramProject:
        return ProgramProject(
            id=model.id,
            tenant_id=model.tenant_id,
            created_at=model.created_at,
            updated_at=model.updated_at,
            program_id=model.program_id,
            project_id=model.project_id,
            role=ProgramProjectRole(model.role),
            priority=model.priority,
            notes=model.notes or "",
        )

    @staticmethod
    def to_model(entity: ProgramProject, model: ProgramProjectModel = None) -> ProgramProjectModel:
        if model is None:
            model = ProgramProjectModel()
        model.id = entity.id
        model.program_id = entity.program_id
        model.project_id = entity.project_id
        model.role = entity.role.value if isinstance(entity.role, ProgramProjectRole) else entity.role
        model.priority = entity.priority
        model.notes = entity.notes
        return model


class BusinessCaseMapper:
    """Mapper: BusinessCase Entity ↔ BusinessCaseModel."""

    @staticmethod
    def to_entity(model: BusinessCaseModel) -> BusinessCase:
        return BusinessCase(
            id=model.id,
            tenant_id=model.tenant_id,
            created_at=model.created_at,
            updated_at=model.updated_at,
            title=model.title,
            description=model.description or "",
            program_id=model.program_id,
            project_id=model.project_id,
            status=BusinessCaseStatus(model.status),
            sponsor_id=model.sponsor_id,
            prepared_by_id=model.prepared_by_id,
            strategic_fit_score=model.strategic_fit_score,
            total_investment=model.total_investment,
            expected_revenue=model.expected_revenue,
            expected_savings=model.expected_savings,
            payback_period_months=model.payback_period_months,
            assumptions=model.assumptions or "",
            constraints=model.constraints or "",
            risks_summary=model.risks_summary or "",
            metadata=model.metadata or {},
        )

    @staticmethod
    def to_model(entity: BusinessCase, model: BusinessCaseModel = None) -> BusinessCaseModel:
        if model is None:
            model = BusinessCaseModel()
        model.id = entity.id
        model.title = entity.title
        model.description = entity.description
        model.program_id = entity.program_id
        model.project_id = entity.project_id
        model.status = entity.status.value if isinstance(entity.status, BusinessCaseStatus) else entity.status
        model.sponsor_id = entity.sponsor_id
        model.prepared_by_id = entity.prepared_by_id
        model.strategic_fit_score = entity.strategic_fit_score
        model.total_investment = entity.total_investment
        model.expected_revenue = entity.expected_revenue
        model.expected_savings = entity.expected_savings
        model.payback_period_months = entity.payback_period_months
        model.assumptions = entity.assumptions
        model.constraints = entity.constraints
        model.risks_summary = entity.risks_summary
        model.metadata = entity.metadata
        return model


class CashFlowProjectionMapper:
    """Mapper: CashFlowProjection Entity ↔ CashFlowProjectionModel."""

    @staticmethod
    def to_entity(model: CashFlowProjectionModel) -> CashFlowProjection:
        return CashFlowProjection(
            id=model.id,
            tenant_id=model.tenant_id,
            created_at=model.created_at,
            updated_at=model.updated_at,
            business_case_id=model.business_case_id,
            year=model.year,
            investment=model.investment,
            revenue=model.revenue,
            savings=model.savings,
            operational_cost=model.operational_cost,
        )

    @staticmethod
    def to_model(entity: CashFlowProjection, model: CashFlowProjectionModel = None) -> CashFlowProjectionModel:
        if model is None:
            model = CashFlowProjectionModel()
        model.id = entity.id
        model.business_case_id = entity.business_case_id
        model.year = entity.year
        model.investment = entity.investment
        model.revenue = entity.revenue
        model.savings = entity.savings
        model.operational_cost = entity.operational_cost
        return model


class ScoringModelMapper:
    """Mapper: ScoringModel Entity ↔ ScoringModelModel."""

    @staticmethod
    def to_entity(model: ScoringModelModel) -> ScoringModelEntity:
        return ScoringModelEntity(
            id=model.id,
            tenant_id=model.tenant_id,
            created_at=model.created_at,
            updated_at=model.updated_at,
            name=model.name,
            description=model.description or "",
            portfolio_id=model.portfolio_id,
            is_active=model.is_active,
            metadata=model.metadata or {},
        )

    @staticmethod
    def to_model(entity: ScoringModelEntity, model: ScoringModelModel = None) -> ScoringModelModel:
        if model is None:
            model = ScoringModelModel()
        model.id = entity.id
        model.name = entity.name
        model.description = entity.description
        model.portfolio_id = entity.portfolio_id
        model.is_active = entity.is_active
        model.metadata = entity.metadata
        return model


class ScoringCriterionMapper:
    """Mapper: ScoringCriterion Entity ↔ ScoringCriterionModel."""

    @staticmethod
    def to_entity(model: ScoringCriterionModel) -> ScoringCriterionEntity:
        return ScoringCriterionEntity(
            id=model.id,
            tenant_id=model.tenant_id,
            created_at=model.created_at,
            updated_at=model.updated_at,
            scoring_model_id=model.scoring_model_id,
            name=model.name,
            description=model.description or "",
            weight=model.weight,
            scale_min=model.scale_min,
            scale_max=model.scale_max,
            category=model.category,
            order=model.order,
        )

    @staticmethod
    def to_model(entity: ScoringCriterionEntity, model: ScoringCriterionModel = None) -> ScoringCriterionModel:
        if model is None:
            model = ScoringCriterionModel()
        model.id = entity.id
        model.scoring_model_id = entity.scoring_model_id
        model.name = entity.name
        model.description = entity.description
        model.weight = entity.weight
        model.scale_min = entity.scale_min
        model.scale_max = entity.scale_max
        model.category = entity.category
        model.order = entity.order
        return model


class ProjectScoreMapper:
    """Mapper: ProjectScore Entity ↔ ProjectScoreModel."""

    @staticmethod
    def to_entity(model: ProjectScoreModel) -> ProjectScoreEntity:
        return ProjectScoreEntity(
            id=model.id,
            tenant_id=model.tenant_id,
            created_at=model.created_at,
            updated_at=model.updated_at,
            scoring_model_id=model.scoring_model_id,
            project_id=model.project_id,
            criterion_id=model.criterion_id,
            score=model.score,
            justification=model.justification or "",
            scored_by_id=model.scored_by_id,
            scored_at=model.scored_at,
        )

    @staticmethod
    def to_model(entity: ProjectScoreEntity, model: ProjectScoreModel = None) -> ProjectScoreModel:
        if model is None:
            model = ProjectScoreModel()
        model.id = entity.id
        model.scoring_model_id = entity.scoring_model_id
        model.project_id = entity.project_id
        model.criterion_id = entity.criterion_id
        model.score = entity.score
        model.justification = entity.justification
        model.scored_by_id = entity.scored_by_id
        model.scored_at = entity.scored_at
        return model


class BenefitPlanMapper:
    """Mapper: BenefitPlan Entity ↔ BenefitPlanModel."""

    @staticmethod
    def to_entity(model: BenefitPlanModel) -> BenefitPlanEntity:
        return BenefitPlanEntity(
            id=model.id,
            tenant_id=model.tenant_id,
            created_at=model.created_at,
            updated_at=model.updated_at,
            name=model.name,
            description=model.description or "",
            program_id=model.program_id,
            portfolio_id=model.portfolio_id,
            is_active=model.is_active,
            metadata=model.metadata or {},
        )

    @staticmethod
    def to_model(entity: BenefitPlanEntity, model: BenefitPlanModel = None) -> BenefitPlanModel:
        if model is None:
            model = BenefitPlanModel()
        model.id = entity.id
        model.name = entity.name
        model.description = entity.description
        model.program_id = entity.program_id
        model.portfolio_id = entity.portfolio_id
        model.is_active = entity.is_active
        model.metadata = entity.metadata
        return model


class BenefitMapper:
    """Mapper: Benefit Entity ↔ BenefitModel."""

    @staticmethod
    def to_entity(model: BenefitModel) -> BenefitEntity:
        return BenefitEntity(
            id=model.id,
            tenant_id=model.tenant_id,
            created_at=model.created_at,
            updated_at=model.updated_at,
            benefit_plan_id=model.benefit_plan_id,
            title=model.title,
            description=model.description or "",
            benefit_type=BenefitType(model.benefit_type),
            category=model.category or "",
            target_value=model.target_value,
            actual_value=model.actual_value,
            measurement_method=model.measurement_method or "",
            frequency=MeasurementFrequency(model.frequency),
            owner_id=model.owner_id,
            status=BenefitStatus(model.status),
            target_date=model.target_date,
            metadata=model.metadata or {},
        )

    @staticmethod
    def to_model(entity: BenefitEntity, model: BenefitModel = None) -> BenefitModel:
        if model is None:
            model = BenefitModel()
        model.id = entity.id
        model.benefit_plan_id = entity.benefit_plan_id
        model.title = entity.title
        model.description = entity.description
        model.benefit_type = entity.benefit_type.value if isinstance(entity.benefit_type, BenefitType) else entity.benefit_type
        model.category = entity.category
        model.target_value = entity.target_value
        model.actual_value = entity.actual_value
        model.measurement_method = entity.measurement_method
        model.frequency = entity.frequency.value if isinstance(entity.frequency, MeasurementFrequency) else entity.frequency
        model.owner_id = entity.owner_id
        model.status = entity.status.value if isinstance(entity.status, BenefitStatus) else entity.status
        model.target_date = entity.target_date
        model.metadata = entity.metadata
        return model


class BenefitMeasurementMapper:
    """Mapper: BenefitMeasurement Entity ↔ BenefitMeasurementModel."""

    @staticmethod
    def to_entity(model: BenefitMeasurementModel) -> BenefitMeasurementEntity:
        return BenefitMeasurementEntity(
            id=model.id,
            tenant_id=model.tenant_id,
            created_at=model.created_at,
            updated_at=model.updated_at,
            benefit_id=model.benefit_id,
            measurement_date=model.measurement_date,
            value=model.value,
            notes=model.notes or "",
            measured_by_id=model.measured_by_id,
        )

    @staticmethod
    def to_model(entity: BenefitMeasurementEntity, model: BenefitMeasurementModel = None) -> BenefitMeasurementModel:
        if model is None:
            model = BenefitMeasurementModel()
        model.id = entity.id
        model.benefit_id = entity.benefit_id
        model.measurement_date = entity.measurement_date
        model.value = entity.value
        model.notes = entity.notes
        model.measured_by_id = entity.measured_by_id
        return model


class CapacityPlanMapper:
    """Mapper: CapacityPlan Entity ↔ CapacityPlanModel."""

    @staticmethod
    def to_entity(model: CapacityPlanModel) -> CapacityPlanEntity:
        return CapacityPlanEntity(
            id=model.id,
            tenant_id=model.tenant_id,
            created_at=model.created_at,
            updated_at=model.updated_at,
            name=model.name,
            description=model.description or "",
            portfolio_id=model.portfolio_id,
            period_start=model.period_start,
            period_end=model.period_end,
            is_active=model.is_active,
            metadata=model.metadata or {},
        )

    @staticmethod
    def to_model(entity: CapacityPlanEntity, model: CapacityPlanModel = None) -> CapacityPlanModel:
        if model is None:
            model = CapacityPlanModel()
        model.id = entity.id
        model.name = entity.name
        model.description = entity.description
        model.portfolio_id = entity.portfolio_id
        model.period_start = entity.period_start
        model.period_end = entity.period_end
        model.is_active = entity.is_active
        model.metadata = entity.metadata
        return model


class ResourceDemandMapper:
    """Mapper: ResourceDemand Entity ↔ ResourceDemandModel."""

    @staticmethod
    def to_entity(model: ResourceDemandModel) -> ResourceDemandEntity:
        return ResourceDemandEntity(
            id=model.id,
            tenant_id=model.tenant_id,
            created_at=model.created_at,
            updated_at=model.updated_at,
            capacity_plan_id=model.capacity_plan_id,
            resource_type=model.resource_type,
            project_id=model.project_id,
            demand_hours=model.demand_hours,
            period=model.period,
            notes=model.notes or "",
        )

    @staticmethod
    def to_model(entity: ResourceDemandEntity, model: ResourceDemandModel = None) -> ResourceDemandModel:
        if model is None:
            model = ResourceDemandModel()
        model.id = entity.id
        model.capacity_plan_id = entity.capacity_plan_id
        model.resource_type = entity.resource_type
        model.project_id = entity.project_id
        model.demand_hours = entity.demand_hours
        model.period = entity.period
        model.notes = entity.notes
        return model


class ResourceSupplyMapper:
    """Mapper: ResourceSupply Entity ↔ ResourceSupplyModel."""

    @staticmethod
    def to_entity(model: ResourceSupplyModel) -> ResourceSupplyEntity:
        return ResourceSupplyEntity(
            id=model.id,
            tenant_id=model.tenant_id,
            created_at=model.created_at,
            updated_at=model.updated_at,
            capacity_plan_id=model.capacity_plan_id,
            resource_type=model.resource_type,
            available_hours=model.available_hours,
            period=model.period,
            notes=model.notes or "",
        )

    @staticmethod
    def to_model(entity: ResourceSupplyEntity, model: ResourceSupplyModel = None) -> ResourceSupplyModel:
        if model is None:
            model = ResourceSupplyModel()
        model.id = entity.id
        model.capacity_plan_id = entity.capacity_plan_id
        model.resource_type = entity.resource_type
        model.available_hours = entity.available_hours
        model.period = entity.period
        model.notes = entity.notes
        return model


class CrossProjectDependencyMapper:
    """Mapper: CrossProjectDependency Entity ↔ CrossProjectDependencyModel."""

    @staticmethod
    def to_entity(model) -> CrossProjectDependencyEntity:
        return CrossProjectDependencyEntity(
            id=model.id,
            tenant_id=model.tenant_id,
            created_at=model.created_at,
            updated_at=model.updated_at,
            predecessor_project_id=model.predecessor_project_id,
            successor_project_id=model.successor_project_id,
            predecessor_milestone_id=model.predecessor_milestone_id,
            successor_milestone_id=model.successor_milestone_id,
            dependency_type=model.dependency_type,
            lag_days=model.lag_days,
            status=model.status,
            impact_description=model.impact_description or "",
            metadata=model.metadata or {},
        )

    @staticmethod
    def to_model(entity: CrossProjectDependencyEntity, model=None):
        from ..persistence.models import CrossProjectDependencyModel
        if model is None:
            model = CrossProjectDependencyModel()
        model.id = entity.id
        model.predecessor_project_id = entity.predecessor_project_id
        model.successor_project_id = entity.successor_project_id
        model.predecessor_milestone_id = entity.predecessor_milestone_id
        model.successor_milestone_id = entity.successor_milestone_id
        model.dependency_type = entity.dependency_type
        model.lag_days = entity.lag_days
        model.status = entity.status
        model.impact_description = entity.impact_description
        model.metadata = entity.metadata
        return model
