"""Services."""
from __future__ import annotations
from uuid import UUID
from sqlalchemy.ext.asyncio import AsyncSession
from app.events.publisher import get_event_publisher
from app.events.types import BeautyBusinessEventType
from app.models.types import AuditAction
from app.services.audit_service import AuditService
from app.validators import validate_code
from shared.exceptions import AppError, NotFoundError
from shared.security import CurrentUser

def _actor(actor):
    return actor.user_id if actor else None


from uuid import UUID

from sqlalchemy.ext.asyncio import AsyncSession

from app.events.publisher import get_event_publisher
from app.events.types import BeautyBusinessEventType
from app.models.marketing import MarketingCampaignRef
from app.models.types import AuditAction
from app.repositories.marketing import MarketingCampaignRefRepository
from app.schemas.marketing import MarketingCampaignRefCreate
from app.services.audit_service import AuditService
from app.validators import validate_code
from shared.exceptions import AppError, NotFoundError
from shared.security import CurrentUser


def _actor(actor: CurrentUser | None) -> str | None:
    return actor.user_id if actor else None


class MarketingCampaignRefService:
    def __init__(self, session: AsyncSession) -> None:
        self.session = session
        self.repo = MarketingCampaignRefRepository(session)
        self.audit = AuditService(session)
        self.events = get_event_publisher()

    async def create(self, tenant_id: UUID, body: MarketingCampaignRefCreate, *, actor: CurrentUser | None = None) -> MarketingCampaignRef:
        code = validate_code(body.code)
        if await self.repo.get_by_code(tenant_id, code):
            raise AppError("کد تکراری است", error_code="duplicate_code", status_code=409)
        data = body.model_dump()
        entity = MarketingCampaignRef(tenant_id=tenant_id, **data, created_by=_actor(actor), updated_by=_actor(actor))
        await self.repo.add(entity)
        await self.audit.record(
            tenant_id=tenant_id,
            entity_type="marketing_campaign_ref",
            entity_id=entity.id,
            action=AuditAction.CREATE,
            actor_user_id=_actor(actor),
            changes=data,
        )
        self.events.publish(
            event_type=BeautyBusinessEventType.MARKETING_CAMPAIGN_CREATED,
            aggregate_type="marketing_campaign_ref",
            aggregate_id=entity.id,
            tenant_id=tenant_id,
            payload={"id": str(entity.id)},
        )
        await self.session.commit()
        await self.session.refresh(entity)
        return entity

    async def get(self, tenant_id: UUID, entity_id: UUID) -> MarketingCampaignRef:
        entity = await self.repo.get(tenant_id, entity_id)
        if entity is None:
            raise NotFoundError("مورد یافت نشد")
        return entity

    async def list(self, tenant_id: UUID, *, offset: int = 0, limit: int = 20):
        return await self.repo.list_by_tenant(tenant_id, offset=offset, limit=limit)

    async def soft_delete(self, tenant_id: UUID, entity_id: UUID, *, actor: CurrentUser | None = None) -> MarketingCampaignRef:
        entity = await self.get(tenant_id, entity_id)
        await self.repo.soft_delete(entity, deleted_by=_actor(actor))
        await self.audit.record(
            tenant_id=tenant_id,
            entity_type="marketing_campaign_ref",
            entity_id=entity.id,
            action=AuditAction.DELETE,
            actor_user_id=_actor(actor),
        )
        await self.session.commit()
        await self.session.refresh(entity)
        return entity


from uuid import UUID

from sqlalchemy.ext.asyncio import AsyncSession

from app.events.publisher import get_event_publisher
from app.events.types import BeautyBusinessEventType
from app.models.marketing import LoyaltyIntegrationConfig
from app.models.types import AuditAction
from app.repositories.marketing import LoyaltyIntegrationConfigRepository
from app.schemas.marketing import LoyaltyIntegrationConfigCreate
from app.services.audit_service import AuditService
from app.validators import validate_code
from shared.exceptions import AppError, NotFoundError
from shared.security import CurrentUser


def _actor(actor: CurrentUser | None) -> str | None:
    return actor.user_id if actor else None


class LoyaltyIntegrationConfigService:
    def __init__(self, session: AsyncSession) -> None:
        self.session = session
        self.repo = LoyaltyIntegrationConfigRepository(session)
        self.audit = AuditService(session)
        self.events = get_event_publisher()

    async def create(self, tenant_id: UUID, body: LoyaltyIntegrationConfigCreate, *, actor: CurrentUser | None = None) -> LoyaltyIntegrationConfig:
        code = validate_code(body.code)
        if await self.repo.get_by_code(tenant_id, code):
            raise AppError("کد تکراری است", error_code="duplicate_code", status_code=409)
        data = body.model_dump()
        entity = LoyaltyIntegrationConfig(tenant_id=tenant_id, **data, created_by=_actor(actor), updated_by=_actor(actor))
        await self.repo.add(entity)
        await self.audit.record(
            tenant_id=tenant_id,
            entity_type="loyalty_integration_config",
            entity_id=entity.id,
            action=AuditAction.CREATE,
            actor_user_id=_actor(actor),
            changes=data,
        )
        self.events.publish(
            event_type=BeautyBusinessEventType.LOYALTY_INTEGRATION_CONFIG_CREATED,
            aggregate_type="loyalty_integration_config",
            aggregate_id=entity.id,
            tenant_id=tenant_id,
            payload={"id": str(entity.id)},
        )
        await self.session.commit()
        await self.session.refresh(entity)
        return entity

    async def get(self, tenant_id: UUID, entity_id: UUID) -> LoyaltyIntegrationConfig:
        entity = await self.repo.get(tenant_id, entity_id)
        if entity is None:
            raise NotFoundError("مورد یافت نشد")
        return entity

    async def list(self, tenant_id: UUID, *, offset: int = 0, limit: int = 20):
        return await self.repo.list_by_tenant(tenant_id, offset=offset, limit=limit)

    async def soft_delete(self, tenant_id: UUID, entity_id: UUID, *, actor: CurrentUser | None = None) -> LoyaltyIntegrationConfig:
        entity = await self.get(tenant_id, entity_id)
        await self.repo.soft_delete(entity, deleted_by=_actor(actor))
        await self.audit.record(
            tenant_id=tenant_id,
            entity_type="loyalty_integration_config",
            entity_id=entity.id,
            action=AuditAction.DELETE,
            actor_user_id=_actor(actor),
        )
        await self.session.commit()
        await self.session.refresh(entity)
        return entity


from uuid import UUID

from sqlalchemy.ext.asyncio import AsyncSession

from app.events.publisher import get_event_publisher
from app.events.types import BeautyBusinessEventType
from app.models.marketing import CommunicationIntegrationConfig
from app.models.types import AuditAction
from app.repositories.marketing import CommunicationIntegrationConfigRepository
from app.schemas.marketing import CommunicationIntegrationConfigCreate
from app.services.audit_service import AuditService
from app.validators import validate_code
from shared.exceptions import AppError, NotFoundError
from shared.security import CurrentUser


def _actor(actor: CurrentUser | None) -> str | None:
    return actor.user_id if actor else None


class CommunicationIntegrationConfigService:
    def __init__(self, session: AsyncSession) -> None:
        self.session = session
        self.repo = CommunicationIntegrationConfigRepository(session)
        self.audit = AuditService(session)
        self.events = get_event_publisher()

    async def create(self, tenant_id: UUID, body: CommunicationIntegrationConfigCreate, *, actor: CurrentUser | None = None) -> CommunicationIntegrationConfig:
        code = validate_code(body.code)
        if await self.repo.get_by_code(tenant_id, code):
            raise AppError("کد تکراری است", error_code="duplicate_code", status_code=409)
        data = body.model_dump()
        entity = CommunicationIntegrationConfig(tenant_id=tenant_id, **data, created_by=_actor(actor), updated_by=_actor(actor))
        await self.repo.add(entity)
        await self.audit.record(
            tenant_id=tenant_id,
            entity_type="communication_integration_config",
            entity_id=entity.id,
            action=AuditAction.CREATE,
            actor_user_id=_actor(actor),
            changes=data,
        )
        self.events.publish(
            event_type=BeautyBusinessEventType.COMMUNICATION_INTEGRATION_CONFIG_CREATED,
            aggregate_type="communication_integration_config",
            aggregate_id=entity.id,
            tenant_id=tenant_id,
            payload={"id": str(entity.id)},
        )
        await self.session.commit()
        await self.session.refresh(entity)
        return entity

    async def get(self, tenant_id: UUID, entity_id: UUID) -> CommunicationIntegrationConfig:
        entity = await self.repo.get(tenant_id, entity_id)
        if entity is None:
            raise NotFoundError("مورد یافت نشد")
        return entity

    async def list(self, tenant_id: UUID, *, offset: int = 0, limit: int = 20):
        return await self.repo.list_by_tenant(tenant_id, offset=offset, limit=limit)

    async def soft_delete(self, tenant_id: UUID, entity_id: UUID, *, actor: CurrentUser | None = None) -> CommunicationIntegrationConfig:
        entity = await self.get(tenant_id, entity_id)
        await self.repo.soft_delete(entity, deleted_by=_actor(actor))
        await self.audit.record(
            tenant_id=tenant_id,
            entity_type="communication_integration_config",
            entity_id=entity.id,
            action=AuditAction.DELETE,
            actor_user_id=_actor(actor),
        )
        await self.session.commit()
        await self.session.refresh(entity)
        return entity


from app.models.marketing import IntegrationDispatch
from app.models.types import IntegrationDispatchStatus
from app.providers.mocks import (
    MockAccountingProvider,
    MockCommunicationProvider,
    MockCRMProvider,
    MockDeliveryProvider,
    MockExperienceProvider,
    MockLoyaltyProvider,
)
from app.repositories.marketing import IntegrationDispatchRepository
from app.schemas.marketing import IntegrationDispatchCreate
from app.validators import validate_non_empty

_MOCK_DISPATCHERS = {
    "accounting": MockAccountingProvider(),
    "crm": MockCRMProvider(),
    "loyalty": MockLoyaltyProvider(),
    "communication": MockCommunicationProvider(),
    "delivery": MockDeliveryProvider(),
    "experience": MockExperienceProvider(),
}


class IntegrationDispatchService:
    def __init__(self, session: AsyncSession) -> None:
        self.session = session
        self.repo = IntegrationDispatchRepository(session)
        self.audit = AuditService(session)
        self.events = get_event_publisher()

    async def create(
        self,
        tenant_id: UUID,
        body: IntegrationDispatchCreate,
        *,
        actor: CurrentUser | None = None,
    ) -> IntegrationDispatch:
        event_type = validate_non_empty(body.event_type, field="event_type")
        kind = validate_non_empty(body.integration_kind, field="integration_kind")
        dispatcher = _MOCK_DISPATCHERS.get(kind)
        response: dict = {"ok": True, "provider": "mock", "kind": kind}
        status = IntegrationDispatchStatus.SENT
        if dispatcher is None:
            status = IntegrationDispatchStatus.FAILED
            response = {"ok": False, "reason": "unknown_integration_kind"}
        elif hasattr(dispatcher, "send"):
            await dispatcher.send(
                tenant_id=tenant_id,
                channel="default",
                template_key=event_type,
                to=body.payload_ref or "",
                payload={"event_type": event_type},
            )
        elif hasattr(dispatcher, "create_receivable_ref"):
            response = await dispatcher.create_receivable_ref(
                tenant_id=tenant_id,
                reference=body.payload_ref or event_type,
                payload={"event_type": event_type},
            )
        elif hasattr(dispatcher, "create_delivery_ref"):
            response = await dispatcher.create_delivery_ref(
                tenant_id=tenant_id,
                reference=body.payload_ref or event_type,
                payload={"event_type": event_type},
            )
        elif hasattr(dispatcher, "resolve_contact"):
            response = await dispatcher.resolve_contact(
                tenant_id=tenant_id, contact_ref=body.payload_ref or event_type
            )
        elif hasattr(dispatcher, "resolve_member"):
            response = await dispatcher.resolve_member(
                tenant_id=tenant_id, member_ref=body.payload_ref or event_type
            )
        elif hasattr(dispatcher, "resolve_site"):
            response = await dispatcher.resolve_site(
                tenant_id=tenant_id, site_ref=body.payload_ref or event_type
            )

        entity = IntegrationDispatch(
            tenant_id=tenant_id,
            organization_id=body.organization_id,
            integration_kind=kind,
            config_id=body.config_id,
            event_type=event_type,
            payload_ref=body.payload_ref,
            status=status,
            response_json=response,
            created_by=_actor(actor),
            updated_by=_actor(actor),
        )
        await self.repo.add(entity)
        await self.audit.record(
            tenant_id=tenant_id,
            entity_type="integration_dispatch",
            entity_id=entity.id,
            action=AuditAction.DISPATCH,
            actor_user_id=_actor(actor),
            changes=body.model_dump(),
        )
        self.events.publish(
            event_type=BeautyBusinessEventType.INTEGRATION_DISPATCH_CREATED,
            aggregate_type="integration_dispatch",
            aggregate_id=entity.id,
            tenant_id=tenant_id,
            payload={"status": entity.status.value, "event_type": entity.event_type},
        )
        await self.session.commit()
        await self.session.refresh(entity)
        return entity

    async def get(self, tenant_id: UUID, entity_id: UUID) -> IntegrationDispatch:
        entity = await self.repo.get(tenant_id, entity_id)
        if entity is None:
            raise NotFoundError("ارسال یکپارچه‌سازی یافت نشد")
        return entity

    async def list(self, tenant_id: UUID, *, offset: int = 0, limit: int = 20):
        return await self.repo.list_by_tenant(tenant_id, offset=offset, limit=limit)
