"""
Strategy Application Service — Stage-Gate Governance.
"""

from typing import List, Optional
from uuid import UUID

from ..dtos.strategy_dtos import (
    GateProcessCreateDTO, GateProcessUpdateDTO, GateProcessResponseDTO,
    GateCreateDTO, GateUpdateDTO, GateResponseDTO,
    GateReviewCreateDTO, GateReviewUpdateDTO, GateReviewResponseDTO,
    GateReviewerCreateDTO, GateReviewerUpdateDTO, GateReviewerResponseDTO,
    GateDeliverableCreateDTO, GateDeliverableUpdateDTO, GateDeliverableResponseDTO,
)
from ...domain.entities.gate_process import GateProcess
from ...domain.entities.gate import Gate
from ...domain.entities.gate_review import GateReview
from ...domain.entities.gate_reviewer import GateReviewer
from ...domain.entities.gate_deliverable import GateDeliverable
from ...infrastructure.repositories import (
    DjangoGateProcessRepository,
    DjangoGateRepository,
    DjangoGateReviewRepository,
    DjangoGateReviewerRepository,
    DjangoGateDeliverableRepository,
)
from apps.core.event_bus.events import event_bus
from ...domain.events.strategy_events import (
    GateReviewStarted, GateReviewPassed, GateReviewFailed, GateReviewConditional,
)


class StageGateService:
    """سرویس لایه اپلیکیشن — مدیریت Stage-Gate Governance."""

    def __init__(self):
        self.process_repo = DjangoGateProcessRepository()
        self.gate_repo = DjangoGateRepository()
        self.review_repo = DjangoGateReviewRepository()
        self.reviewer_repo = DjangoGateReviewerRepository()
        self.deliverable_repo = DjangoGateDeliverableRepository()

    # ─── Gate Process CRUD ────────────────────────

    def create_process(self, dto: GateProcessCreateDTO) -> GateProcessResponseDTO:
        entity = GateProcess(
            name=dto.name,
            description=dto.description,
            applicable_to=dto.applicable_to,
            tenant_id=dto.tenant_id,
        )
        saved = self.process_repo.save(entity)
        return self._process_to_dto(saved)

    def update_process(self, process_id: UUID, dto: GateProcessUpdateDTO) -> Optional[GateProcessResponseDTO]:
        entity = self.process_repo.find_by_id(process_id)
        if not entity:
            return None
        if dto.name is not None:
            entity.name = dto.name
        if dto.description is not None:
            entity.description = dto.description
        if dto.applicable_to is not None:
            entity.applicable_to = dto.applicable_to
        if dto.is_active is not None:
            entity.is_active = dto.is_active
        saved = self.process_repo.save(entity)
        return self._process_to_dto(saved)

    def get_process(self, process_id: UUID) -> Optional[GateProcessResponseDTO]:
        entity = self.process_repo.find_by_id(process_id)
        return self._process_to_dto(entity) if entity else None

    def delete_process(self, process_id: UUID) -> bool:
        return self.process_repo.delete(process_id)

    # ─── Gate CRUD ────────────────────────────────

    def create_gate(self, dto: GateCreateDTO) -> GateResponseDTO:
        entity = Gate(
            process_id=dto.process_id,
            name=dto.name,
            sequence=dto.sequence,
            description=dto.description,
            mandatory_deliverables=dto.mandatory_deliverables,
            criteria=dto.criteria,
            tenant_id=dto.tenant_id,
        )
        saved = self.gate_repo.save(entity)
        return self._gate_to_dto(saved)

    def update_gate(self, gate_id: UUID, dto: GateUpdateDTO) -> Optional[GateResponseDTO]:
        entity = self.gate_repo.find_by_id(gate_id)
        if not entity:
            return None
        if dto.name is not None:
            entity.name = dto.name
        if dto.sequence is not None:
            entity.sequence = dto.sequence
        if dto.description is not None:
            entity.description = dto.description
        if dto.mandatory_deliverables is not None:
            entity.mandatory_deliverables = dto.mandatory_deliverables
        if dto.criteria is not None:
            entity.criteria = dto.criteria
        saved = self.gate_repo.save(entity)
        return self._gate_to_dto(saved)

    def get_gate(self, gate_id: UUID) -> Optional[GateResponseDTO]:
        entity = self.gate_repo.find_by_id(gate_id)
        return self._gate_to_dto(entity) if entity else None

    def delete_gate(self, gate_id: UUID) -> bool:
        return self.gate_repo.delete(gate_id)

    # ─── Gate Review CRUD ─────────────────────────

    def create_review(self, dto: GateReviewCreateDTO) -> GateReviewResponseDTO:
        entity = GateReview(
            gate_id=dto.gate_id,
            entity_id=dto.entity_id,
            entity_type=dto.entity_type,
            review_date=dto.review_date,
            notes=dto.notes,
            tenant_id=dto.tenant_id,
        )
        saved = self.review_repo.save(entity)
        return self._review_to_dto(saved)

    def update_review(self, review_id: UUID, dto: GateReviewUpdateDTO) -> Optional[GateReviewResponseDTO]:
        entity = self.review_repo.find_by_id(review_id)
        if not entity:
            return None
        if dto.status is not None:
            entity.status = dto.status
        if dto.review_date is not None:
            entity.review_date = dto.review_date
        if dto.notes is not None:
            entity.notes = dto.notes
        saved = self.review_repo.save(entity)
        return self._review_to_dto(saved)

    def start_review(self, review_id: UUID) -> Optional[GateReviewResponseDTO]:
        entity = self.review_repo.find_by_id(review_id)
        if not entity:
            return None
        entity.start_review()
        saved = self.review_repo.save(entity)
        event_bus.publish(GateReviewStarted(
            tenant_id=str(entity.tenant_id),
            review_id=entity.id,
            gate_id=entity.gate_id,
        ))
        return self._review_to_dto(saved)

    def approve_review(self, review_id: UUID) -> Optional[GateReviewResponseDTO]:
        entity = self.review_repo.find_by_id(review_id)
        if not entity:
            return None
        entity.approve()
        saved = self.review_repo.save(entity)
        event_bus.publish(GateReviewPassed(
            tenant_id=str(entity.tenant_id),
            review_id=entity.id,
            gate_id=entity.gate_id,
        ))
        return self._review_to_dto(saved)

    def approve_conditional(self, review_id: UUID) -> Optional[GateReviewResponseDTO]:
        entity = self.review_repo.find_by_id(review_id)
        if not entity:
            return None
        entity.approve_conditional()
        saved = self.review_repo.save(entity)
        event_bus.publish(GateReviewConditional(
            tenant_id=str(entity.tenant_id),
            review_id=entity.id,
            gate_id=entity.gate_id,
        ))
        return self._review_to_dto(saved)

    def reject_review(self, review_id: UUID) -> Optional[GateReviewResponseDTO]:
        entity = self.review_repo.find_by_id(review_id)
        if not entity:
            return None
        entity.reject()
        saved = self.review_repo.save(entity)
        event_bus.publish(GateReviewFailed(
            tenant_id=str(entity.tenant_id),
            review_id=entity.id,
            gate_id=entity.gate_id,
        ))
        return self._review_to_dto(saved)

    def delete_review(self, review_id: UUID) -> bool:
        return self.review_repo.delete(review_id)

    # ─── Gate Reviewer CRUD ───────────────────────

    def create_reviewer(self, dto: GateReviewerCreateDTO) -> GateReviewerResponseDTO:
        entity = GateReviewer(
            review_id=dto.review_id,
            user_id=dto.user_id,
            role=dto.role,
            tenant_id=dto.tenant_id,
        )
        saved = self.reviewer_repo.save(entity)
        return self._reviewer_to_dto(saved)

    def update_reviewer(self, reviewer_id: UUID, dto: GateReviewerUpdateDTO) -> Optional[GateReviewerResponseDTO]:
        entity = self.reviewer_repo.find_by_id(reviewer_id)
        if not entity:
            return None
        if dto.decision is not None:
            entity.decision = dto.decision
        if dto.comments is not None:
            entity.comments = dto.comments
        saved = self.reviewer_repo.save(entity)
        return self._reviewer_to_dto(saved)

    def delete_reviewer(self, reviewer_id: UUID) -> bool:
        return self.reviewer_repo.delete(reviewer_id)

    # ─── Gate Deliverable CRUD ────────────────────

    def create_deliverable(self, dto: GateDeliverableCreateDTO) -> GateDeliverableResponseDTO:
        entity = GateDeliverable(
            review_id=dto.review_id,
            name=dto.name,
            document_id=dto.document_id,
            tenant_id=dto.tenant_id,
        )
        saved = self.deliverable_repo.save(entity)
        return self._deliverable_to_dto(saved)

    def update_deliverable(self, deliverable_id: UUID, dto: GateDeliverableUpdateDTO) -> Optional[GateDeliverableResponseDTO]:
        entity = self.deliverable_repo.find_by_id(deliverable_id)
        if not entity:
            return None
        if dto.name is not None:
            entity.name = dto.name
        if dto.status is not None:
            entity.status = dto.status
        if dto.document_id is not None:
            entity.document_id = dto.document_id
        saved = self.deliverable_repo.save(entity)
        return self._deliverable_to_dto(saved)

    def delete_deliverable(self, deliverable_id: UUID) -> bool:
        return self.deliverable_repo.delete(deliverable_id)

    # ─── Mappers ──────────────────────────────────

    @staticmethod
    def _process_to_dto(entity: GateProcess) -> GateProcessResponseDTO:
        return GateProcessResponseDTO(
            id=entity.id,
            name=entity.name,
            description=entity.description,
            applicable_to=entity.applicable_to,
            is_active=entity.is_active,
            created_at=entity.created_at,
            updated_at=entity.updated_at,
        )

    @staticmethod
    def _gate_to_dto(entity: Gate) -> GateResponseDTO:
        return GateResponseDTO(
            id=entity.id,
            process_id=entity.process_id,
            name=entity.name,
            sequence=entity.sequence,
            description=entity.description,
            mandatory_deliverables=entity.mandatory_deliverables,
            criteria=entity.criteria,
            created_at=entity.created_at,
            updated_at=entity.updated_at,
        )

    @staticmethod
    def _review_to_dto(entity: GateReview) -> GateReviewResponseDTO:
        return GateReviewResponseDTO(
            id=entity.id,
            gate_id=entity.gate_id,
            entity_id=entity.entity_id,
            entity_type=entity.entity_type,
            status=entity.status,
            review_date=entity.review_date,
            notes=entity.notes,
            created_at=entity.created_at,
            updated_at=entity.updated_at,
        )

    @staticmethod
    def _reviewer_to_dto(entity: GateReviewer) -> GateReviewerResponseDTO:
        return GateReviewerResponseDTO(
            id=entity.id,
            review_id=entity.review_id,
            user_id=entity.user_id,
            role=entity.role,
            decision=entity.decision,
            comments=entity.comments,
            created_at=entity.created_at,
            updated_at=entity.updated_at,
        )

    @staticmethod
    def _deliverable_to_dto(entity: GateDeliverable) -> GateDeliverableResponseDTO:
        return GateDeliverableResponseDTO(
            id=entity.id,
            review_id=entity.review_id,
            name=entity.name,
            status=entity.status,
            document_id=entity.document_id,
            created_at=entity.created_at,
            updated_at=entity.updated_at,
        )
