import XCTest
@testable import damus

final class ContactCardManagerTests: XCTestCase {

    func testInitialization() {
        // Given: The shared ContactCardManager instance
        let manager = ContactCardManager()

        // Then: It should have an empty favorites set
        XCTAssertTrue(manager.favorites.isEmpty)
    }

    func testIsFavorite_WhenEmpty_ReturnsFalse() {
        // Given: An empty favorites manager
        let sut = ContactCardManager()
        let pubkey = Pubkey(hex: "32e1827635450ebb3c5a7d12c1f8e7b2b514439ac10a67eef3d9fd9c5c68e245")!
        
        // When: Checking if a pubkey is favorite
        let result = sut.isFavorite(pubkey)
        
        // Then: Should return false
        XCTAssertFalse(result)
    }
    
    @MainActor
    func testIsFavorite_WhenPubkeyExists_ReturnsTrue() {
        // Given: A pubkey added to favorites
        let sut = ContactCardManager()
        let pubkey = Pubkey(hex: "32e1827635450ebb3c5a7d12c1f8e7b2b514439ac10a67eef3d9fd9c5c68e245")!
        sut.toggleFavorite(pubkey, postbox: test_damus_state.nostrNetwork.postbox, keyPair: generate_new_keypair())
        
        // When: Checking if the pubkey is favorite
        let result = sut.isFavorite(pubkey)
        
        // Then: Should return true
        XCTAssertTrue(result)
    }
    
    @MainActor
    func testIsFavorite_WhenPubkeyDoesNotExist_ReturnsFalse() {
        // Given: A different pubkey added to favorites
        let sut = ContactCardManager()
        let expected = Pubkey(hex: "32e1827635450ebb3c5a7d12c1f8e7b2b514439ac10a67eef3d9fd9c5c68e245")!
        let differentPubkey = Pubkey(hex: "5b0183ab6c3e322bf4d41c6b3aef98562a144847b7499543727c5539a114563e")!
        sut.toggleFavorite(expected, postbox: test_damus_state.nostrNetwork.postbox, keyPair: generate_new_keypair())
        
        // When: Checking if a different pubkey is favorite
        let result = sut.isFavorite(differentPubkey)
        
        // Then: Should return false
        XCTAssertFalse(result)
    }

    @MainActor
    func testToggleFavorite_WhenNotFavorite_AddsToFavorites() {
        // Given: A pubkey not in favorites
        let sut = ContactCardManager()
        let pubkey = Pubkey(hex: "32e1827635450ebb3c5a7d12c1f8e7b2b514439ac10a67eef3d9fd9c5c68e245")!
        XCTAssertFalse(sut.isFavorite(pubkey))
        
        // When: Toggling the pubkey
        sut.toggleFavorite(pubkey, postbox: test_damus_state.nostrNetwork.postbox, keyPair: generate_new_keypair())
        
        // Then: Should be added to favorites
        XCTAssertTrue(sut.isFavorite(pubkey))
        XCTAssertEqual(sut.favorites.count, 1)
    }
    
    @MainActor
    func testToggleFavorite_WhenAlreadyFavorite_RemovesFromFavorites() {
        // Given: A pubkey already in favorites
        let sut = ContactCardManager()
        let pubkey = Pubkey(hex: "32e1827635450ebb3c5a7d12c1f8e7b2b514439ac10a67eef3d9fd9c5c68e245")!
        let keypair = generate_new_keypair()
        sut.toggleFavorite(pubkey, postbox: test_damus_state.nostrNetwork.postbox, keyPair: keypair)
        XCTAssertTrue(sut.isFavorite(pubkey))
        
        // When: Toggling the pubkey again
        sut.toggleFavorite(pubkey, postbox: test_damus_state.nostrNetwork.postbox, keyPair: keypair)
        
        // Then: Should be removed from favorites
        XCTAssertFalse(sut.isFavorite(pubkey))
        XCTAssertEqual(sut.favorites.count, 0)
    }

    func testloadEvent_WithContactCard_AddsToFavorites() {
        // Given: A contact card event for favorites
        let sut = ContactCardManager()
        let targetPubkey = Pubkey(hex: "32e1827635450ebb3c5a7d12c1f8e7b2b514439ac10a67eef3d9fd9c5c68e245")!
        let userPubkey = Pubkey(hex: "5b0183ab6c3e322bf4d41c6b3aef98562a144847b7499543727c5539a114563e")!

        let tags = [
            [ContactCardManager.TARGET_PUBLIC_KEY, targetPubkey.hex()],
            [ContactCardManager.CONTACT_SET, ContactCardManager.FAVORITE_TAG]
        ]

        let event = NostrEvent(
            content: "",
            keypair: Keypair(pubkey: userPubkey, privkey: nil),
            kind: NostrKind.contact_card.rawValue,
            tags: tags
        )!

        // When: Handling the contact card event
        sut.loadEvent(event, pubkey: userPubkey)

        // Then: Should add the target pubkey to favorites
        XCTAssertTrue(sut.isFavorite(targetPubkey))
    }

    @MainActor
    func testloadEvent_WithContactCard_RemovesFromFavorites() {
        // Given: A contact card event without favorite tag (unfavorite)
        let sut = ContactCardManager()
        let targetPubkey = Pubkey(hex: "32e1827635450ebb3c5a7d12c1f8e7b2b514439ac10a67eef3d9fd9c5c68e245")!
        let userPubkey = Pubkey(hex: "5b0183ab6c3e322bf4d41c6b3aef98562a144847b7499543727c5539a114563e")!

        // First add to favorites
        sut.toggleFavorite(targetPubkey, postbox: test_damus_state.nostrNetwork.postbox, keyPair: generate_new_keypair())
        XCTAssertTrue(sut.isFavorite(targetPubkey))

        // Create unfavorite contact card (only target public key tag, no contact set tag)
        let tags = [
            [ContactCardManager.TARGET_PUBLIC_KEY, targetPubkey.hex()]
        ]

        let event = NostrEvent(
            content: "",
            keypair: Keypair(pubkey: userPubkey, privkey: nil),
            kind: NostrKind.contact_card.rawValue,
            tags: tags,
            createdAt: UInt32(Date().timeIntervalSince1970) + 1
        )!

        // When: Handling the unfavorite contact card event
        sut.loadEvent(event, pubkey: userPubkey)

        // Then: Should remove the target pubkey from favorites
        XCTAssertFalse(sut.isFavorite(targetPubkey))
    }

    func testloadEvent_WithMissingTargetPubkey_ReturnsEarly() {
        // Given: A contact card event without d tag (missing target pubkey)
        let sut = ContactCardManager()
        let userPubkey = Pubkey(hex: "5b0183ab6c3e322bf4d41c6b3aef98562a144847b7499543727c5539a114563e")!
        let initialFavoritesCount = sut.favorites.count
        let tags = [
            [ContactCardManager.CONTACT_SET, ContactCardManager.FAVORITE_TAG]
        ]
        let event = NostrEvent(
            content: "",
            keypair: Keypair(pubkey: userPubkey, privkey: nil),
            kind: NostrKind.contact_card.rawValue,
            tags: tags
        )!

        // When: Handling the event with missing target pubkey
        sut.loadEvent(event, pubkey: userPubkey)

        // Then: Should return early without changing favorites
        XCTAssertEqual(sut.favorites.count, initialFavoritesCount)
    }

    func testloadEvent_WithInvalidTargetPubkey_ReturnsEarly() {
        // Given: A contact card event with invalid d tag (invalid pubkey hex)
        let sut = ContactCardManager()
        let userPubkey = Pubkey(hex: "5b0183ab6c3e322bf4d41c6b3aef98562a144847b7499543727c5539a114563e")!
        let initialFavoritesCount = sut.favorites.count
        // Create contact card with invalid pubkey hex
        let tags = [
            [ContactCardManager.TARGET_PUBLIC_KEY, "invalid_hex"],
            [ContactCardManager.CONTACT_SET, ContactCardManager.FAVORITE_TAG]
        ]
        let event = NostrEvent(
            content: "",
            keypair: Keypair(pubkey: userPubkey, privkey: nil),
            kind: NostrKind.contact_card.rawValue,
            tags: tags
        )!

        // When: Handling the event with invalid target pubkey
        sut.loadEvent(event, pubkey: userPubkey)

        // Then: Should return early without changing favorites
        XCTAssertEqual(sut.favorites.count, initialFavoritesCount)
    }

    func testloadEvent_WithOlderEvent_ReturnsEarly() {
        // Given: An existing newer contact card event
        let sut = ContactCardManager()
        let targetPubkey = Pubkey(hex: "32e1827635450ebb3c5a7d12c1f8e7b2b514439ac10a67eef3d9fd9c5c68e245")!
        let userPubkey = Pubkey(hex: "5b0183ab6c3e322bf4d41c6b3aef98562a144847b7499543727c5539a114563e")!
        // Create newer favorite event first
        let newerTags = [
            [ContactCardManager.TARGET_PUBLIC_KEY, targetPubkey.hex()],
            [ContactCardManager.CONTACT_SET, ContactCardManager.FAVORITE_TAG]
        ]
        let newerEvent = NostrEvent(
            content: "",
            keypair: Keypair(pubkey: userPubkey, privkey: nil),
            kind: NostrKind.contact_card.rawValue,
            tags: newerTags,
            createdAt: 1000
        )!
        sut.loadEvent(newerEvent, pubkey: userPubkey)
        XCTAssertTrue(sut.isFavorite(targetPubkey))
        // Create older unfavorite event
        let olderTags = [
            [ContactCardManager.TARGET_PUBLIC_KEY, targetPubkey.hex()]
        ]
        let olderEvent = NostrEvent(
            content: "",
            keypair: Keypair(pubkey: userPubkey, privkey: nil),
            kind: NostrKind.contact_card.rawValue,
            tags: olderTags,
            createdAt: 500  // Older timestamp
        )!

        // When: Handling the older event
        sut.loadEvent(olderEvent, pubkey: userPubkey)

        // Then: Should ignore the older event and keep the favorite status
        XCTAssertTrue(sut.isFavorite(targetPubkey))
    }

    @MainActor
    func testFilter_WithFavoritePubkey_ReturnsTrue() {
        // Given: A pubkey in favorites
        let sut = ContactCardManager()
        let favoritePubkey = Pubkey(hex: "32e1827635450ebb3c5a7d12c1f8e7b2b514439ac10a67eef3d9fd9c5c68e245")!
        let otherPubkey = Pubkey(hex: "5b0183ab6c3e322bf4d41c6b3aef98562a144847b7499543727c5539a114563e")!
        sut.toggleFavorite(favoritePubkey, postbox: test_damus_state.nostrNetwork.postbox, keyPair: generate_new_keypair())
        // Create events from both pubkeys
        let favoriteEvent = NostrEvent(
            content: "Hello from favorite",
            keypair: Keypair(pubkey: favoritePubkey, privkey: nil),
            kind: NostrKind.text.rawValue,
            tags: []
        )!
        let otherEvent = NostrEvent(
            content: "Hello from other",
            keypair: Keypair(pubkey: otherPubkey, privkey: nil),
            kind: NostrKind.text.rawValue,
            tags: []
        )!

        // When: Using the filter
        let filter = sut.filter

        // Then: Should return true for favorite, false for other
        XCTAssertTrue(filter(favoriteEvent))
        XCTAssertFalse(filter(otherEvent))
    }
}
