import Foundation

/// Call signaling packet (0x1A / 26).
/// Wire format mirrors server/desktop legacy signaling:
/// `signalType` always first, then short-form for busy/disconnected/ringingTimeout,
/// otherwise `src`, `dst`, optional `sharedPublic`, optional `callId/joinToken`,
/// optional `roomId` for create-room fallback compatibility.
enum SignalType: Int, Sendable {
    case call = 0
    case keyExchange = 1
    case activeCall = 2
    case endCall = 3
    case createRoom = 4
    case endCallBecausePeerDisconnected = 5
    case endCallBecauseBusy = 6
    case accept = 7
    case ringingTimeout = 8
}

struct PacketSignalPeer: Packet {
    static let packetId = 0x1A

    var src: String = ""
    var dst: String = ""
    var sharedPublic: String = ""
    var signalType: SignalType = .call
    var callId: String = ""
    var joinToken: String = ""
    var roomId: String = ""

    func write(to stream: Stream) {
        stream.writeInt8(signalType.rawValue)
        if isShortSignal {
            return
        }
        stream.writeString(src)
        stream.writeString(dst)
        if signalType == .keyExchange {
            stream.writeString(sharedPublic)
        }
        if hasLegacyCallMetadata {
            stream.writeString(callId)
            stream.writeString(joinToken)
        }
        // Signal code 4 is mode-aware:
        // - legacy ACTIVE: no roomId on wire
        // - create-room fallback: roomId present on wire
        if signalType == .createRoom && !roomId.isEmpty {
            stream.writeString(roomId)
        }
    }

    mutating func read(from stream: Stream) {
        src = ""
        dst = ""
        sharedPublic = ""
        callId = ""
        joinToken = ""
        roomId = ""
        signalType = SignalType(rawValue: stream.readInt8()) ?? .call
        if isShortSignal {
            return
        }
        src = stream.readString()
        dst = stream.readString()
        if signalType == .keyExchange {
            sharedPublic = stream.readString()
        }
        if hasLegacyCallMetadata {
            callId = stream.readString()
            joinToken = stream.readString()
        }
        // Signal code 4 is mode-aware on read:
        // - empty roomId => legacy ACTIVE
        // - non-empty roomId => create-room fallback
        if signalType == .createRoom {
            roomId = stream.readString()
        }
    }

    private var isShortSignal: Bool {
        signalType == .endCallBecauseBusy
            || signalType == .endCallBecausePeerDisconnected
            || signalType == .ringingTimeout
    }

    private var hasLegacyCallMetadata: Bool {
        signalType == .call || signalType == .accept || signalType == .endCall
    }
}
