"""
Strategy Application — Strategy Map Service.
"""

import uuid
from typing import List, Optional
from uuid import UUID

from ..dtos.strategy_dtos import (
    StrategyMapCreateDTO, StrategyMapUpdateDTO, StrategyMapResponseDTO,
    PerspectiveCreateDTO, PerspectiveUpdateDTO, PerspectiveResponseDTO,
    MapObjectiveCreateDTO, MapObjectiveUpdateDTO, MapObjectiveResponseDTO,
    CausalLinkCreateDTO, CausalLinkUpdateDTO, CausalLinkResponseDTO,
)
from ...domain.entities.strategy_map import StrategyMap
from ...domain.entities.strategy_map_perspective import StrategyMapPerspective
from ...domain.entities.strategy_map_objective import StrategyMapObjective
from ...domain.entities.causal_link import CausalLink
from ...infrastructure.repositories import (
    DjangoStrategyMapRepository,
    DjangoStrategyMapPerspectiveRepository,
    DjangoStrategyMapObjectiveRepository,
    DjangoCausalLinkRepository,
)


class StrategyMapService:
    """سرویس مدیریت نقشه راهبردی."""

    def __init__(self):
        self.map_repo = DjangoStrategyMapRepository()
        self.perspective_repo = DjangoStrategyMapPerspectiveRepository()
        self.objective_repo = DjangoStrategyMapObjectiveRepository()
        self.link_repo = DjangoCausalLinkRepository()

    # ─── Strategy Map CRUD ────────────────────────

    def create_map(self, dto: StrategyMapCreateDTO) -> StrategyMapResponseDTO:
        entity = StrategyMap(
            id=uuid.uuid4(),
            tenant_id=dto.tenant_id,
            name=dto.name,
            description=dto.description,
            metadata=dto.metadata or {},
        )
        saved = self.map_repo.save(entity)
        return self._map_to_response(saved)

    def get_map(self, map_id: UUID) -> Optional[StrategyMapResponseDTO]:
        entity = self.map_repo.find_by_id(map_id)
        if not entity:
            return None
        return self._map_to_response(entity)

    def update_map(self, map_id: UUID, dto: StrategyMapUpdateDTO) -> Optional[StrategyMapResponseDTO]:
        entity = self.map_repo.find_by_id(map_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.metadata is not None:
            entity.metadata = dto.metadata
        saved = self.map_repo.save(entity)
        return self._map_to_response(saved)

    def delete_map(self, map_id: UUID) -> bool:
        return self.map_repo.delete(map_id)

    def publish_map(self, map_id: UUID) -> Optional[StrategyMapResponseDTO]:
        entity = self.map_repo.find_by_id(map_id)
        if not entity:
            return None
        entity.publish()
        saved = self.map_repo.save(entity)
        return self._map_to_response(saved)

    def archive_map(self, map_id: UUID) -> Optional[StrategyMapResponseDTO]:
        entity = self.map_repo.find_by_id(map_id)
        if not entity:
            return None
        entity.archive()
        saved = self.map_repo.save(entity)
        return self._map_to_response(saved)

    def new_version(self, map_id: UUID) -> Optional[StrategyMapResponseDTO]:
        entity = self.map_repo.find_by_id(map_id)
        if not entity:
            return None
        entity.new_version()
        saved = self.map_repo.save(entity)
        return self._map_to_response(saved)

    # ─── Perspective CRUD ─────────────────────────

    def create_perspective(self, dto: PerspectiveCreateDTO) -> PerspectiveResponseDTO:
        entity = StrategyMapPerspective(
            id=uuid.uuid4(),
            tenant_id=dto.tenant_id,
            map_id=dto.map_id,
            name=dto.name,
            description=dto.description,
            sort_order=dto.sort_order,
            color=dto.color,
            metadata=dto.metadata or {},
        )
        saved = self.perspective_repo.save(entity)
        return self._perspective_to_response(saved)

    def get_perspective(self, perspective_id: UUID) -> Optional[PerspectiveResponseDTO]:
        entity = self.perspective_repo.find_by_id(perspective_id)
        if not entity:
            return None
        return self._perspective_to_response(entity)

    def update_perspective(self, perspective_id: UUID, dto: PerspectiveUpdateDTO) -> Optional[PerspectiveResponseDTO]:
        entity = self.perspective_repo.find_by_id(perspective_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.sort_order is not None:
            entity.sort_order = dto.sort_order
        if dto.color is not None:
            entity.color = dto.color
        if dto.metadata is not None:
            entity.metadata = dto.metadata
        saved = self.perspective_repo.save(entity)
        return self._perspective_to_response(saved)

    def delete_perspective(self, perspective_id: UUID) -> bool:
        return self.perspective_repo.delete(perspective_id)

    def list_perspectives(self, map_id: UUID) -> List[PerspectiveResponseDTO]:
        entities = self.perspective_repo.find_by_map(map_id)
        return [self._perspective_to_response(e) for e in entities]

    # ─── Map Objective CRUD ───────────────────────

    def create_map_objective(self, dto: MapObjectiveCreateDTO) -> MapObjectiveResponseDTO:
        entity = StrategyMapObjective(
            id=uuid.uuid4(),
            tenant_id=dto.tenant_id,
            map_id=dto.map_id,
            perspective_id=dto.perspective_id,
            objective_id=dto.objective_id,
            x_position=dto.x_position,
            y_position=dto.y_position,
            metadata=dto.metadata or {},
        )
        saved = self.objective_repo.save(entity)
        return self._map_objective_to_response(saved)

    def get_map_objective(self, obj_id: UUID) -> Optional[MapObjectiveResponseDTO]:
        entity = self.objective_repo.find_by_id(obj_id)
        if not entity:
            return None
        return self._map_objective_to_response(entity)

    def update_map_objective(self, obj_id: UUID, dto: MapObjectiveUpdateDTO) -> Optional[MapObjectiveResponseDTO]:
        entity = self.objective_repo.find_by_id(obj_id)
        if not entity:
            return None
        if dto.perspective_id is not None:
            entity.perspective_id = dto.perspective_id
        if dto.x_position is not None:
            entity.x_position = dto.x_position
        if dto.y_position is not None:
            entity.y_position = dto.y_position
        if dto.metadata is not None:
            entity.metadata = dto.metadata
        saved = self.objective_repo.save(entity)
        return self._map_objective_to_response(saved)

    def delete_map_objective(self, obj_id: UUID) -> bool:
        return self.objective_repo.delete(obj_id)

    def list_map_objectives(self, map_id: UUID) -> List[MapObjectiveResponseDTO]:
        entities = self.objective_repo.find_by_map(map_id)
        return [self._map_objective_to_response(e) for e in entities]

    # ─── Causal Link CRUD ─────────────────────────

    def create_link(self, dto: CausalLinkCreateDTO) -> CausalLinkResponseDTO:
        entity = CausalLink(
            id=uuid.uuid4(),
            tenant_id=dto.tenant_id,
            map_id=dto.map_id,
            from_objective_id=dto.from_objective_id,
            to_objective_id=dto.to_objective_id,
            strength=dto.strength,
            description=dto.description,
            metadata=dto.metadata or {},
        )
        saved = self.link_repo.save(entity)
        return self._link_to_response(saved)

    def get_link(self, link_id: UUID) -> Optional[CausalLinkResponseDTO]:
        entity = self.link_repo.find_by_id(link_id)
        if not entity:
            return None
        return self._link_to_response(entity)

    def update_link(self, link_id: UUID, dto: CausalLinkUpdateDTO) -> Optional[CausalLinkResponseDTO]:
        entity = self.link_repo.find_by_id(link_id)
        if not entity:
            return None
        if dto.strength is not None:
            entity.strength = dto.strength
        if dto.description is not None:
            entity.description = dto.description
        if dto.metadata is not None:
            entity.metadata = dto.metadata
        saved = self.link_repo.save(entity)
        return self._link_to_response(saved)

    def delete_link(self, link_id: UUID) -> bool:
        return self.link_repo.delete(link_id)

    def list_links(self, map_id: UUID) -> List[CausalLinkResponseDTO]:
        entities = self.link_repo.find_by_map(map_id)
        return [self._link_to_response(e) for e in entities]

    # ─── Mappers ──────────────────────────────────

    @staticmethod
    def _map_to_response(e: StrategyMap) -> StrategyMapResponseDTO:
        return StrategyMapResponseDTO(
            id=e.id, tenant_id=e.tenant_id,
            name=e.name, description=e.description,
            version=e.version, status=e.status,
            metadata=e.metadata,
            created_at=e.created_at, updated_at=e.updated_at,
        )

    @staticmethod
    def _perspective_to_response(e: StrategyMapPerspective) -> PerspectiveResponseDTO:
        return PerspectiveResponseDTO(
            id=e.id, tenant_id=e.tenant_id,
            map_id=e.map_id, name=e.name,
            description=e.description, sort_order=e.sort_order,
            color=e.color, metadata=e.metadata,
            created_at=e.created_at, updated_at=e.updated_at,
        )

    @staticmethod
    def _map_objective_to_response(e: StrategyMapObjective) -> MapObjectiveResponseDTO:
        return MapObjectiveResponseDTO(
            id=e.id, tenant_id=e.tenant_id,
            map_id=e.map_id, perspective_id=e.perspective_id,
            objective_id=e.objective_id,
            x_position=e.x_position, y_position=e.y_position,
            metadata=e.metadata,
            created_at=e.created_at, updated_at=e.updated_at,
        )

    @staticmethod
    def _link_to_response(e: CausalLink) -> CausalLinkResponseDTO:
        return CausalLinkResponseDTO(
            id=e.id, tenant_id=e.tenant_id,
            map_id=e.map_id,
            from_objective_id=e.from_objective_id,
            to_objective_id=e.to_objective_id,
            strength=e.strength, description=e.description,
            metadata=e.metadata,
            created_at=e.created_at, updated_at=e.updated_at,
        )
