from __future__ import annotations

from collections.abc import Callable, Iterable, Mapping
from dataclasses import dataclass, field
from datetime import datetime
from typing import Protocol, runtime_checkable


CAPABILITY_CAMPAIGNS_MAIL_POLICY_CONTEXT = "campaigns.mailPolicyContext"
CAPABILITY_CAMPAIGNS_ACCESS = "campaigns.access"
CAPABILITY_CAMPAIGNS_POLICY_CONTEXT = "campaigns.policyContext"
CAPABILITY_CAMPAIGNS_DELIVERY_TASKS = "campaigns.deliveryTasks"
CAPABILITY_CAMPAIGNS_RETENTION = "campaigns.retention"


@dataclass(frozen=True, slots=True)
class CampaignMailPolicyContext:
    id: str
    tenant_id: str
    owner_user_id: str | None = None
    owner_group_id: str | None = None
    mail_profile_policy: Mapping[str, object] = field(default_factory=dict)


@dataclass(frozen=True, slots=True)
class CampaignPolicyContext:
    id: str
    tenant_id: str
    owner_user_id: str | None = None
    owner_group_id: str | None = None
    settings: Mapping[str, object] = field(default_factory=dict)


@runtime_checkable
class CampaignMailPolicyContextProvider(Protocol):
    def get_campaign_mail_policy_context(
        self,
        session: object,
        *,
        tenant_id: str,
        campaign_id: str,
    ) -> CampaignMailPolicyContext | None:
        ...

    def set_campaign_mail_profile_policy(
        self,
        session: object,
        *,
        tenant_id: str,
        campaign_id: str,
        policy: Mapping[str, object],
    ) -> Mapping[str, object]:
        ...


@runtime_checkable
class CampaignAccessProvider(Protocol):
    def campaign_exists(self, session: object, *, tenant_id: str, campaign_id: str) -> bool:
        ...

    def can_read_campaign(
        self,
        session: object,
        *,
        tenant_id: str,
        campaign_id: str,
        user_id: str,
        group_ids: Iterable[str] = (),
        tenant_admin: bool = False,
    ) -> bool:
        ...


@runtime_checkable
class CampaignPolicyContextProvider(Protocol):
    def get_campaign_policy_context(
        self,
        session: object,
        *,
        tenant_id: str | None = None,
        campaign_id: str,
    ) -> CampaignPolicyContext | None:
        ...

    def set_campaign_settings(
        self,
        session: object,
        *,
        tenant_id: str,
        campaign_id: str,
        settings: Mapping[str, object],
    ) -> Mapping[str, object]:
        ...


@runtime_checkable
class CampaignDeliveryTaskProvider(Protocol):
    def send_campaign_job(self, session: object, *, job_id: str, enqueue_imap_task: bool = True) -> Mapping[str, object]:
        ...

    def append_sent_for_job(self, session: object, *, job_id: str) -> Mapping[str, object]:
        ...


@runtime_checkable
class CampaignRetentionProvider(Protocol):
    def apply_retention(
        self,
        session: object,
        *,
        dry_run: bool,
        now: datetime,
        policy_for_campaign_id: Callable[[str | None], object],
    ) -> Mapping[str, Mapping[str, int]]:
        ...
