// ResumeCursor.swift
// Persists the last-seen IC-1 sequence number per session across app launches.
//
// Storage: UserDefaults (standard suite).
// Key schema: "ResumeCursor.<sessionId>"
//
// Thread-safety: UserDefaults is documented as thread-safe; this class adds no
// additional synchronisation. All callers are expected to be on the main actor
// in practice (alongside WebSocketClient), though no actor annotation is
// imposed here so tests can call freely.

import Foundation

/// Stores and retrieves the last acknowledged IC-1 sequence number for each
/// session, enabling resume-from-cursor on reconnect.
public final class ResumeCursor {

    private let defaults: UserDefaults
    private let keyPrefix = "ResumeCursor."

    // MARK: - Init

    /// Creates a cursor backed by `defaults` (defaults to `.standard`).
    public init(defaults: UserDefaults = .standard) {
        self.defaults = defaults
    }

    // MARK: - Public API

    /// Returns the last persisted sequence number for `sessionId`, or `nil`
    /// if no sequence has been stored (i.e. first-ever connection).
    public func lastSeq(for sessionId: String) -> UInt64? {
        let key = storageKey(for: sessionId)
        // UserDefaults stores integer values as Int64 (signed). We use the raw
        // bit pattern to round-trip UInt64 without loss, since UInt64.max
        // exceeds Int.max on 64-bit platforms.
        guard defaults.object(forKey: key) != nil else { return nil }
        let raw = defaults.integer(forKey: key)
        return UInt64(bitPattern: Int64(raw))
    }

    /// Persists `seq` as the latest acknowledged chunk for `sessionId`.
    ///
    /// Called after successfully processing a `BinaryFrame` so that the cursor
    /// always reflects a frame that the app has actually consumed.
    public func update(sessionId: String, seq: UInt64) {
        // Store as Int64 bit-pattern; see note in lastSeq(for:).
        let raw = Int(Int64(bitPattern: seq))
        defaults.set(raw, forKey: storageKey(for: sessionId))
    }

    /// Removes the stored cursor for `sessionId`.
    ///
    /// Call this when a session is deleted or a full snapshot has been received
    /// and the client no longer needs delta replay.
    public func clear(sessionId: String) {
        defaults.removeObject(forKey: storageKey(for: sessionId))
    }

    // MARK: - Private

    private func storageKey(for sessionId: String) -> String {
        keyPrefix + sessionId
    }
}
