import ActivityKit
import XCTest
@testable import Rosetta

@MainActor
final class CallLiveActivityTests: XCTestCase {
    private let ownKey = "02-own-la-test"
    private let peerKey = "02-peer-la-test"

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

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

    // MARK: - CallActivityAttributes

    func testCallActivityAttributesCodableRoundTrip() throws {
        let attrs = CallActivityAttributes(
            peerName: "Test User",
            peerPublicKey: "02abcdef1234567890",
            colorIndex: 5
        )

        let encoder = JSONEncoder()
        let data = try encoder.encode(attrs)
        let decoder = JSONDecoder()
        let decoded = try decoder.decode(CallActivityAttributes.self, from: data)

        XCTAssertEqual(decoded.peerName, "Test User")
        XCTAssertEqual(decoded.peerPublicKey, "02abcdef1234567890")
        XCTAssertEqual(decoded.colorIndex, 5)
    }

    func testCallActivityContentStateCodableRoundTrip() throws {
        let state = CallActivityAttributes.ContentState(
            durationSec: 125,
            isActive: true,
            isMuted: false
        )

        let data = try JSONEncoder().encode(state)
        let decoded = try JSONDecoder().decode(CallActivityAttributes.ContentState.self, from: data)

        XCTAssertEqual(decoded.durationSec, 125)
        XCTAssertTrue(decoded.isActive)
        XCTAssertFalse(decoded.isMuted)
    }

    func testAttributesSizeUnderActivityKitLimit() throws {
        // ActivityKit limit is ~4KB for attributes
        let attrs = CallActivityAttributes(
            peerName: "Very Long Name That Could Be A Display Name In Any Language",
            peerPublicKey: "02abcdef1234567890abcdef1234567890abcdef1234567890abcdef1234567890",
            colorIndex: 10
        )

        let data = try JSONEncoder().encode(attrs)
        XCTAssertLessThan(data.count, 4096, "Attributes must be under 4KB ActivityKit limit")
    }

    // MARK: - Color Index Parity

    func testColorIndexMatchesRosettaColors() {
        let names = ["Alice", "Bob", "Enfants Riches", "Baragoz", "0333331", ""]
        let keys = ["02abc", "02def", "02123", "02456", "02789", "02000"]

        for (name, key) in zip(names, keys) {
            let expected = RosettaColors.avatarColorIndex(for: name, publicKey: key)
            XCTAssertGreaterThanOrEqual(expected, 0)
            XCTAssertLessThan(expected, RosettaColors.avatarColors.count)
        }
    }

    func testColorIndexDeterministic() {
        let idx1 = RosettaColors.avatarColorIndex(for: "Test", publicKey: "02abc")
        let idx2 = RosettaColors.avatarColorIndex(for: "Test", publicKey: "02abc")
        XCTAssertEqual(idx1, idx2, "Color index must be deterministic")
    }

    func testColorIndexDiffersForDifferentNames() {
        let idx1 = RosettaColors.avatarColorIndex(for: "Alice", publicKey: "02abc")
        let idx2 = RosettaColors.avatarColorIndex(for: "Bob", publicKey: "02abc")
        // Not guaranteed to differ for all inputs, but these specific ones should
        // Just verify they're valid
        XCTAssertGreaterThanOrEqual(idx1, 0)
        XCTAssertGreaterThanOrEqual(idx2, 0)
    }

    func testColorIndexFallsBackToPublicKey() {
        let idxEmpty = RosettaColors.avatarColorIndex(for: "", publicKey: "02abcdef")
        let idxSpaces = RosettaColors.avatarColorIndex(for: "   ", publicKey: "02abcdef")
        XCTAssertEqual(idxEmpty, idxSpaces, "Empty name and spaces-only should both fall back to publicKey")
    }

    // MARK: - Initials Parity

    func testInitialsTwoWords() {
        let result = RosettaColors.initials(name: "Enfants Riches", publicKey: "02abc")
        XCTAssertEqual(result, "ER")
    }

    func testInitialsSingleWord() {
        let result = RosettaColors.initials(name: "Baragoz", publicKey: "02abc")
        XCTAssertEqual(result, "BA")
    }

    func testInitialsEmptyName() {
        let result = RosettaColors.initials(name: "", publicKey: "02abcdef")
        XCTAssertEqual(result, "02")
    }

    func testInitialsThreeWords() {
        let result = RosettaColors.initials(name: "John Paul Smith", publicKey: "02abc")
        XCTAssertEqual(result, "JP")
    }

    // MARK: - Live Activity Lifecycle

    func testLiveActivityCreatedOnOutgoingCall() {
        let result = CallManager.shared.startOutgoingCall(
            toPublicKey: peerKey,
            title: "Peer",
            username: "peer"
        )
        XCTAssertEqual(result, .started)
        // Live Activity may or may not start depending on simulator capabilities
        // but the code path should not crash
    }

    func testLiveActivityCreatedOnIncomingCall() {
        let packet = PacketSignalPeer(
            src: peerKey,
            dst: ownKey,
            sharedPublic: "",
            signalType: .call,
            roomId: ""
        )
        CallManager.shared.testHandleSignalPacket(packet)
        XCTAssertEqual(CallManager.shared.uiState.phase, .incoming)
        // Live Activity start called without crash
    }

    func testLiveActivityEndedOnFinishCall() {
        _ = CallManager.shared.startOutgoingCall(
            toPublicKey: peerKey,
            title: "Peer",
            username: "peer"
        )
        CallManager.shared.endCall()
        XCTAssertNil(CallManager.shared.liveActivity, "Live Activity should be nil after endCall")
    }

    // MARK: - App Group Avatar

    func testAppGroupAvatarFileCreatedAndCleaned() {
        guard let containerURL = FileManager.default.containerURL(
            forSecurityApplicationGroupIdentifier: "group.com.rosetta.dev"
        ) else {
            // App Group not available in test environment — skip
            return
        }

        let avatarURL = containerURL.appendingPathComponent("call_avatar.jpg")

        // Clean up
        try? FileManager.default.removeItem(at: avatarURL)
        XCTAssertFalse(FileManager.default.fileExists(atPath: avatarURL.path))

        // Write test data
        let testData = Data([0xFF, 0xD8, 0xFF, 0xE0]) // JPEG header
        try? testData.write(to: avatarURL)
        XCTAssertTrue(FileManager.default.fileExists(atPath: avatarURL.path))

        // Clean up
        try? FileManager.default.removeItem(at: avatarURL)
        XCTAssertFalse(FileManager.default.fileExists(atPath: avatarURL.path))
    }

    // MARK: - Sound Manager

    func testCallSoundManagerSingleton() {
        let a = CallSoundManager.shared
        let b = CallSoundManager.shared
        XCTAssertTrue(a === b, "CallSoundManager must be singleton")
    }
}
