"""Integration entity for external service connections."""

from __future__ import annotations

from dataclasses import dataclass, field
from datetime import datetime
from enum import StrEnum
from uuid import UUID, uuid4

from noteflow.domain.utils.time import utc_now
from noteflow.infrastructure.logging import log_state_transition


class IntegrationType(StrEnum):
    """Types of integrations supported."""

    AUTH = "auth"
    EMAIL = "email"
    CALENDAR = "calendar"
    PKM = "pkm"
    CUSTOM = "custom"


class IntegrationStatus(StrEnum):
    """Status of an integration connection."""

    DISCONNECTED = "disconnected"
    CONNECTED = "connected"
    ERROR = "error"


@dataclass
class Integration:
    """External service integration entity.

    Represents a connection to an external service like Google Calendar,
    Microsoft Outlook, Notion, etc.
    """

    id: UUID
    workspace_id: UUID
    name: str
    type: IntegrationType
    status: IntegrationStatus = IntegrationStatus.DISCONNECTED
    config: dict[str, object] = field(default_factory=dict)
    last_sync: datetime | None = None
    error_message: str | None = None
    created_at: datetime = field(default_factory=utc_now)
    updated_at: datetime = field(default_factory=utc_now)

    @classmethod
    def create(
        cls,
        workspace_id: UUID,
        name: str,
        integration_type: IntegrationType,
        config: dict[str, object] | None = None,
    ) -> Integration:
        """Create a new integration.

        Args:
            workspace_id: ID of the workspace this integration belongs to.
            name: Display name for the integration.
            integration_type: Type of integration.
            config: Optional configuration dictionary.

        Returns:
            New Integration instance.
        """
        now = utc_now()
        return cls(
            id=uuid4(),
            workspace_id=workspace_id,
            name=name,
            type=integration_type,
            status=IntegrationStatus.DISCONNECTED,
            config=config or {},
            created_at=now,
            updated_at=now,
        )

    def connect(self, provider_email: str | None = None) -> None:
        """Mark integration as connected.

        Args:
            provider_email: Optional email of the authenticated account.
        """
        old_status = self.status
        self.status = IntegrationStatus.CONNECTED
        self.error_message = None
        self.updated_at = utc_now()
        if provider_email:
            self.config["provider_email"] = provider_email
        log_state_transition("integration", str(self.id), old_status, self.status)

    def disconnect(self) -> None:
        """Mark integration as disconnected."""
        old_status = self.status
        self.status = IntegrationStatus.DISCONNECTED
        self.error_message = None
        self.updated_at = utc_now()
        log_state_transition("integration", str(self.id), old_status, self.status)

    def mark_error(self, message: str) -> None:
        """Mark integration as having an error.

        Args:
            message: Error message describing the issue.
        """
        old_status = self.status
        self.status = IntegrationStatus.ERROR
        self.error_message = message
        self.updated_at = utc_now()
        log_state_transition("integration", str(self.id), old_status, self.status, reason="error")

    def record_sync(self) -> None:
        """Record a successful sync timestamp."""
        self.last_sync = utc_now()
        self.updated_at = self.last_sync

    @property
    def is_connected(self) -> bool:
        """Check if integration is connected."""
        return self.status == IntegrationStatus.CONNECTED

    @property
    def provider_email(self) -> str | None:
        """Get the provider email if available."""
        email = self.config.get("provider_email")
        return str(email) if email else None


class SyncRunStatus(StrEnum):
    """Status of a sync operation."""

    RUNNING = "running"
    SUCCESS = "success"
    ERROR = "error"


@dataclass
class SyncRun:
    """Track a single sync operation for an integration.

    Records the status, duration, and results of a synchronization
    operation between NoteFlow and an external service.
    """

    id: UUID
    integration_id: UUID
    status: SyncRunStatus
    started_at: datetime = field(default_factory=utc_now)
    ended_at: datetime | None = None
    duration_ms: int | None = None
    error_message: str | None = None
    stats: dict[str, object] = field(default_factory=dict)

    @classmethod
    def start(cls, integration_id: UUID) -> SyncRun:
        """Start a new sync run.

        Args:
            integration_id: ID of the integration being synced.

        Returns:
            New SyncRun in running status.
        """
        run_id = uuid4()
        started = utc_now()
        return cls(
            id=run_id,
            integration_id=integration_id,
            status=SyncRunStatus.RUNNING,
            started_at=started,
        )

    def complete(self, items_synced: int, items_total: int | None = None) -> None:
        """Mark sync as successfully completed.

        Args:
            items_synced: Number of items synchronized.
            items_total: Total items available (if known).
        """
        old_status = self.status
        now = utc_now()
        self.status = SyncRunStatus.SUCCESS
        self.ended_at = now
        self.duration_ms = int((now - self.started_at).total_seconds() * 1000)
        self.stats["items_synced"] = items_synced
        if items_total is not None:
            self.stats["items_total"] = items_total
        log_state_transition(
            "sync_run", str(self.id), old_status, self.status, items_synced=items_synced
        )

    def fail(self, error_message: str) -> None:
        """Mark sync as failed.

        Args:
            error_message: Description of what went wrong.
        """
        old_status = self.status
        now = utc_now()
        self.status = SyncRunStatus.ERROR
        self.ended_at = now
        self.duration_ms = int((now - self.started_at).total_seconds() * 1000)
        self.error_message = error_message
        log_state_transition("sync_run", str(self.id), old_status, self.status, reason="error")

    @property
    def items_synced(self) -> int:
        """Number of items synchronized."""
        value = self.stats.get("items_synced", 0)
        return int(value) if isinstance(value, (int, str)) else 0

    @property
    def items_total(self) -> int | None:
        """Total items available (if known)."""
        total = self.stats.get("items_total")
        if total is None:
            return None
        return int(total) if isinstance(total, (int, str)) else None

    @property
    def is_running(self) -> bool:
        """Check if sync is still in progress."""
        return self.status == SyncRunStatus.RUNNING
