from __future__ import annotations


from typing import Optional, List, Literal
from dataclasses import dataclass, field


# 3C.3E Transport Hardening Models (must be defined before DesktopPassiveState)

@dataclass
class SessionLifecycleState:
    """Explicit session state machine."""
    state: Literal[
        "not_paired",
        "paired",
        "reconnecting",
        "offline",
        "degraded",
        "revoked",
        "expired",
        "failed"
    ]
    entered_at: int  # when state entered (unix timestamp)
    last_transition_reason: Optional[str] = None
    reconnect_attempts: int = 0  # count of reconnect attempts in current state
    offline_since: Optional[int] = None  # when offline started

@dataclass
class RetryPolicy:
    """Retry and backoff configuration."""
    max_retries: int = 5
    initial_backoff_sec: int = 1
    max_backoff_sec: int = 300  # 5 min max
    backoff_multiplier: float = 2.0
    jitter_fraction: float = 0.1  # 10% jitter
    reconnect_cooldown_sec: int = 60  # min time between reconnect attempts
    transport_timeout_sec: int = 30  # max time for single heartbeat request

@dataclass
class TransportHealthMetrics:
    """Read-only transport observability."""
    heartbeat_success_count: int = 0
    heartbeat_failure_count: int = 0
    heartbeat_latency_ms_avg: Optional[float] = None
    last_heartbeat_latency_ms: Optional[int] = None
    reconnect_count: int = 0
    reconnect_failures: int = 0
    last_transport_error: Optional[str] = None
    last_transport_error_at: Optional[int] = None
    transport_instability_detected: bool = False
    degraded_mode_active: bool = False
    offline_periods: int = 0  # count of offline periods
    total_offline_sec: int = 0  # cumulative offline time

@dataclass
class WarningEscalation:
    """Escalating warning severity based on repeated failures."""
    warning_type: Literal[
        "reconnect_failure",
        "replay_rejection",
        "transport_timeout",
        "stale_heartbeat",
        "signature_invalid"
    ]
    occurrence_count: int = 0
    last_occurrence_at: Optional[int] = None
    severity_level: Literal["info", "low", "medium", "high", "critical"] = "info"
    escalated_at: Optional[int] = None  # when severity increased

@dataclass
class OfflineRecoveryState:
    """Offline persistence and recovery tracking."""
    is_offline: bool = False
    offline_started_at: Optional[int] = None
    pending_heartbeats: int = 0  # queued while offline
    recovery_in_progress: bool = False
    recovery_started_at: Optional[int] = None
    last_successful_heartbeat_before_offline: Optional[int] = None
    stale_recovery_warning_shown: bool = False

@dataclass
class OperationalDiagnostics:
    """Passive runtime diagnostics (read-only)."""
    session_uptime_sec: Optional[int] = None  # since last pairing
    trust_token_expires_in_sec: Optional[int] = None
    reconnect_generation_delta: Optional[int] = None  # expected vs actual
    last_server_contact_at: Optional[int] = None
    server_clock_skew_sec: Optional[int] = None  # estimated
    warnings_aggregated: int = 0  # total warnings across all types
    audit_log_size: int = 0
    last_diagnostic_check_at: Optional[int] = None


@dataclass
class DesktopPassiveState:
    local_device_id: str
    project_identity_uuid: str
    lineage_root_uuid: str
    workspace_root_hash: str
    heartbeat_secret: str
    trust_token: str
    trust_token_expires_at: int
    trust_generation: int = 0
    reconnect_generation: int = 0
    trust_status: str = "paired"
    continuity_badges: list[str] = field(default_factory=list)
    last_heartbeat_ts: int | None = None
    last_error: str | None = None

    # New for 3C.3C visual trust UX
    timeline: List["TimelineEvent"] = field(default_factory=list)
    lineage: List["LineageNode"] = field(default_factory=list)
    warnings: List["Warning"] = field(default_factory=list)
    active_modal: Optional["ModalState"] = None

    # New for 3C.3E transport hardening
    session_lifecycle: Optional[SessionLifecycleState] = None
    retry_policy: RetryPolicy = field(default_factory=RetryPolicy)
    transport_metrics: TransportHealthMetrics = field(default_factory=TransportHealthMetrics)
    warning_escalations: List[WarningEscalation] = field(default_factory=list)
    offline_recovery: OfflineRecoveryState = field(default_factory=OfflineRecoveryState)
    diagnostics: OperationalDiagnostics = field(default_factory=OperationalDiagnostics)


@dataclass
class PairingResult:
    ok: bool
    status: str
    detail: str


@dataclass
class HeartbeatResult:
    ok: bool
    status: str
    continuity_event: str
    reconnect_generation: int
    trust_generation: int
    detail: str = ""


# 3C.3C Visual Trust UX Models

@dataclass
class TimelineEvent:
    timestamp: int
    event_type: Literal[
        "paired", "reconnected", "moved", "forked", "revoked", "restored", "expired"
    ]
    detail: Optional[str] = None

@dataclass
class LineageNode:
    node_id: str
    parent_id: Optional[str]
    status: Literal["active", "revoked", "restored", "forked"]
    is_original: bool = False
    is_current: bool = False
    revoked_at: Optional[int] = None
    restored_at: Optional[int] = None

@dataclass
class Warning:
    warning_id: str
    severity: Literal["info", "low", "medium", "high", "critical"]
    type: Literal[
        "revoked_trust",
        "stale_heartbeat",
        "clone_detected",
        "reconnect_mismatch",
        "expired_trust_token",
        "transport_timeout",
        "transport_instability",
        "offline_recovery_stale",
        "reconnect_cooldown_active",
        "degraded_mode_active",
    ]
    message: str
    acknowledged: bool = False
    timestamp: Optional[int] = None

@dataclass
class ModalState:
    modal_type: Literal[
        "pairing", "revoke_confirm", "restore_confirm", "reconnect_ack", "warning_ack"
    ]
    visible: bool = False
    context: Optional[dict] = None


# 3C.3C Visual Trust UX Models

@dataclass
class TimelineEvent:
    timestamp: int
    event_type: Literal[
        "paired", "reconnected", "moved", "forked", "revoked", "restored", "expired"
    ]
    detail: Optional[str] = None
