"""
Type definitions for user OAuth credentials.

This module defines the core data structures for user-bound OAuth tokens,
including credential status tracking and the main credential dataclass.
"""

from dataclasses import dataclass, field
from datetime import datetime, timezone, timedelta
from enum import Enum
from typing import Any, Dict, List, Optional


class CredentialStatus(Enum):
    """Status of a user OAuth credential."""

    ACTIVE = "active"
    NEEDS_REAUTH = "needs_reauth"
    REVOKED = "revoked"
    EXPIRED = "expired"


@dataclass
class UserOAuthCredential:
    """
    Decrypted user OAuth credential.

    This dataclass represents a user's OAuth tokens for a specific
    external system, with full lifecycle tracking.

    Attributes:
        access_token: The OAuth access token for API calls
        token_type: Token type (usually "Bearer")
        system_key: Identifier for the external system (e.g., "salesforce", "google")
        user_arn: ARN identifying the user who owns this credential
        expires_at: When the access token expires
        scopes: List of OAuth scopes granted
        refresh_token: Optional refresh token for obtaining new access tokens
        metadata: Additional system-specific metadata
        system_user_id: User's ID in the external system
        system_user_email: User's email in the external system
        status: Current credential status
        credential_id: Internal database ID
        tenant_id: Tenant isolation identifier
        last_refreshed_at: When tokens were last refreshed
        last_used_at: When credential was last used for an API call
        created_at: When credential was first stored
        refresh_failure_count: Number of consecutive refresh failures
    """

    # Required fields
    access_token: str
    token_type: str
    system_key: str
    user_arn: str

    # Token lifecycle
    expires_at: Optional[datetime] = None
    scopes: List[str] = field(default_factory=list)
    refresh_token: Optional[str] = None

    # External system info
    metadata: Optional[Dict[str, Any]] = None
    system_user_id: Optional[str] = None
    system_user_email: Optional[str] = None

    # Status tracking
    status: CredentialStatus = CredentialStatus.ACTIVE

    # Internal tracking
    credential_id: Optional[str] = None
    tenant_id: Optional[str] = None
    last_refreshed_at: Optional[datetime] = None
    last_used_at: Optional[datetime] = None
    created_at: Optional[datetime] = None
    refresh_failure_count: int = 0

    @property
    def is_expired(self) -> bool:
        """
        Check if the access token has expired.

        Uses a 60-second buffer to account for clock skew and network latency.
        """
        if not self.expires_at:
            return False
        buffer = timedelta(seconds=60)
        return datetime.now(timezone.utc) >= (self.expires_at - buffer)

    @property
    def needs_refresh(self) -> bool:
        """Check if token needs refresh (expired but has refresh token)."""
        return self.is_expired and self.refresh_token is not None

    @property
    def is_active(self) -> bool:
        """Check if credential is active and not expired."""
        return self.status == CredentialStatus.ACTIVE and not self.is_expired

    def to_auth_header(self) -> str:
        """Return the Authorization header value."""
        return f"{self.token_type} {self.access_token}"

    def to_dict(self) -> Dict[str, Any]:
        """Convert credential to dictionary (excludes sensitive tokens)."""
        return {
            "system_key": self.system_key,
            "user_arn": self.user_arn,
            "token_type": self.token_type,
            "expires_at": self.expires_at.isoformat() if self.expires_at else None,
            "scopes": self.scopes,
            "status": self.status.value,
            "system_user_id": self.system_user_id,
            "system_user_email": self.system_user_email,
            "credential_id": self.credential_id,
            "tenant_id": self.tenant_id,
            "last_refreshed_at": self.last_refreshed_at.isoformat() if self.last_refreshed_at else None,
            "last_used_at": self.last_used_at.isoformat() if self.last_used_at else None,
            "created_at": self.created_at.isoformat() if self.created_at else None,
            "is_expired": self.is_expired,
            "needs_refresh": self.needs_refresh,
        }
