import CryptoKit
import XCTest
@testable import Rosetta

@MainActor
final class CallRoutingTests: XCTestCase {
    private let ownKey = "02-own"
    private let peerA = "02-peer-a"
    private let peerB = "02-peer-b"

    override func setUp() {
        super.setUp()
        CallManager.shared.resetForSessionEnd()
        CallManager.shared.bindAccount(publicKey: ownKey)
    }

    override func tearDown() {
        CallManager.shared.resetForSessionEnd()
        super.tearDown()
    }

    func testIncomingCallMovesToIncomingPhase() {
        let packet = PacketSignalPeer(
            src: peerA,
            dst: ownKey,
            sharedPublic: "",
            signalType: .call,
            roomId: ""
        )

        CallManager.shared.testHandleSignalPacket(packet)

        XCTAssertEqual(CallManager.shared.uiState.phase, .incoming)
        XCTAssertEqual(CallManager.shared.uiState.peerPublicKey, peerA)
    }

    func testBusySignalEndsCurrentCallStateWithoutCrosstalk() {
        CallManager.shared.testSetUiState(
            CallUiState(
                phase: .outgoing,
                peerPublicKey: peerA,
                statusText: "Calling..."
            )
        )

        let packet = PacketSignalPeer(
            src: "",
            dst: "",
            sharedPublic: "",
            signalType: .endCallBecauseBusy,
            roomId: ""
        )
        CallManager.shared.testHandleSignalPacket(packet)

        XCTAssertEqual(CallManager.shared.uiState.phase, .idle)
        XCTAssertEqual(CallManager.shared.uiState.statusText, "User is busy")
        XCTAssertEqual(CallManager.shared.uiState.peerPublicKey, "")
    }

    func testPeerDisconnectedSignalEndsCurrentCallState() {
        CallManager.shared.testSetUiState(
            CallUiState(
                phase: .active,
                peerPublicKey: peerA,
                statusText: "Call active"
            )
        )

        let packet = PacketSignalPeer(
            src: "",
            dst: "",
            sharedPublic: "",
            signalType: .endCallBecausePeerDisconnected,
            roomId: ""
        )
        CallManager.shared.testHandleSignalPacket(packet)

        XCTAssertEqual(CallManager.shared.uiState.phase, .idle)
        XCTAssertEqual(CallManager.shared.uiState.statusText, "Peer disconnected")
        XCTAssertEqual(CallManager.shared.uiState.peerPublicKey, "")
    }

    func testInterleavingForeignSignalDoesNotOverrideActivePeer() {
        CallManager.shared.testSetUiState(
            CallUiState(
                phase: .outgoing,
                peerPublicKey: peerA,
                statusText: "Calling..."
            )
        )

        let foreignPacket = PacketSignalPeer(
            src: peerB,
            dst: ownKey,
            sharedPublic: "",
            signalType: .call,
            roomId: ""
        )
        CallManager.shared.testHandleSignalPacket(foreignPacket)

        XCTAssertEqual(CallManager.shared.uiState.phase, .outgoing)
        XCTAssertEqual(CallManager.shared.uiState.peerPublicKey, peerA)
        XCTAssertEqual(CallManager.shared.uiState.statusText, "Calling...")
    }

    func testLegacyOutgoingFlowCallAcceptKeyExchangeActive() {
        let start = CallManager.shared.startOutgoingCall(
            toPublicKey: peerA,
            title: "Peer A",
            username: "peer_a"
        )
        XCTAssertEqual(start, .started)

        let accept = PacketSignalPeer(
            src: peerA,
            dst: ownKey,
            sharedPublic: "",
            signalType: .accept,
            callId: "call-legacy-1",
            joinToken: "join-legacy-1",
            roomId: ""
        )
        CallManager.shared.testHandleSignalPacket(accept)

        XCTAssertEqual(CallManager.shared.signalingMode, .legacy)
        XCTAssertEqual(CallManager.shared.callId, "call-legacy-1")
        XCTAssertEqual(CallManager.shared.joinToken, "join-legacy-1")
        XCTAssertEqual(CallManager.shared.uiState.phase, .keyExchange)

        let keyExchange = PacketSignalPeer(
            src: peerA,
            dst: ownKey,
            sharedPublic: makePeerPublicHex(),
            signalType: .keyExchange,
            roomId: ""
        )
        CallManager.shared.testHandleSignalPacket(keyExchange)
        XCTAssertEqual(CallManager.shared.uiState.phase, .keyExchange)

        // Legacy ACTIVE arrives as signal code 4 with empty roomId.
        let active = PacketSignalPeer(
            src: peerA,
            dst: ownKey,
            sharedPublic: "",
            signalType: .createRoom,
            roomId: ""
        )
        CallManager.shared.testHandleSignalPacket(active)
        XCTAssertEqual(CallManager.shared.uiState.phase, .webRtcExchange)
    }

    func testCreateRoomFallbackOutgoingFlowKeyExchangeBeforeAccept() {
        let start = CallManager.shared.startOutgoingCall(
            toPublicKey: peerA,
            title: "Peer A",
            username: "peer_a"
        )
        XCTAssertEqual(start, .started)

        let keyExchange = PacketSignalPeer(
            src: peerA,
            dst: ownKey,
            sharedPublic: makePeerPublicHex(),
            signalType: .keyExchange,
            roomId: ""
        )
        CallManager.shared.testHandleSignalPacket(keyExchange)

        XCTAssertEqual(CallManager.shared.signalingMode, .createRoom)
        XCTAssertEqual(CallManager.shared.uiState.phase, .webRtcExchange)
    }

    func testDeferredAcceptCompletesWhenMetadataArrivesLater() {
        CallManager.shared.setupIncomingCallFromPush(
            callerKey: peerA,
            callerName: "Peer A"
        )
        XCTAssertEqual(CallManager.shared.uiState.phase, .incoming)

        let acceptResult = CallManager.shared.acceptIncomingCall()
        XCTAssertEqual(acceptResult, .started)
        XCTAssertTrue(CallManager.shared.pendingIncomingAccept)
        XCTAssertEqual(CallManager.shared.uiState.phase, .keyExchange)
        XCTAssertEqual(CallManager.shared.signalingMode, .undecided)

        let delayedCall = PacketSignalPeer(
            src: peerA,
            dst: ownKey,
            sharedPublic: "",
            signalType: .call,
            callId: "call-delayed-1",
            joinToken: "join-delayed-1",
            roomId: ""
        )
        CallManager.shared.testHandleSignalPacket(delayedCall)

        XCTAssertEqual(CallManager.shared.signalingMode, .legacy)
        XCTAssertFalse(CallManager.shared.pendingIncomingAccept)
        XCTAssertEqual(CallManager.shared.callId, "call-delayed-1")
        XCTAssertEqual(CallManager.shared.joinToken, "join-delayed-1")
        XCTAssertEqual(CallManager.shared.uiState.phase, .keyExchange)
    }

    func testRingingTimeoutSignalTearsDownCall() {
        let start = CallManager.shared.startOutgoingCall(
            toPublicKey: peerA,
            title: "Peer A",
            username: "peer_a"
        )
        XCTAssertEqual(start, .started)

        let timeoutPacket = PacketSignalPeer(
            src: "",
            dst: "",
            sharedPublic: "",
            signalType: .ringingTimeout,
            roomId: ""
        )
        CallManager.shared.testHandleSignalPacket(timeoutPacket)

        XCTAssertEqual(CallManager.shared.uiState.phase, .idle)
        XCTAssertEqual(CallManager.shared.uiState.statusText, "No answer")
    }

    private func makePeerPublicHex() -> String {
        Curve25519.KeyAgreement.PrivateKey().publicKey.rawRepresentation.hexString
    }
}
