//
// ChatViewModelRefactoringTests.swift
// bitchatTests
//
// Pinning tests to characterize ChatViewModel behavior before refactoring.
// These tests act as a safety net to ensure we don't break existing functionality.
//

import Testing
import Foundation
import BitFoundation
@testable import bitchat

struct ChatViewModelRefactoringTests {

    // Helper to setup the environment
    @MainActor
    private func makePinnedViewModel() -> (viewModel: ChatViewModel, transport: MockTransport, identity: MockIdentityManager) {
        let keychain = MockKeychain()
        let keychainHelper = MockKeychainHelper()
        let idBridge = NostrIdentityBridge(keychain: keychainHelper)
        let identityManager = MockIdentityManager(keychain)
        let transport = MockTransport()

        let viewModel = ChatViewModel(
            keychain: keychain,
            idBridge: idBridge,
            identityManager: identityManager,
            transport: transport
        )

        return (viewModel, transport, identityManager)
    }

    // MARK: - Command Processor Integration "Pinning"
    
    @Test @MainActor
    func command_msg_routesToTransport() async throws {
        let (viewModel, transport, _) = makePinnedViewModel()
        
        // Setup: Use simulateConnect so ChatViewModel and UnifiedPeerService are notified
        let peerID = PeerID(str: "0000000000000001")
        transport.simulateConnect(peerID, nickname: "alice")

        let didResolve = await TestHelpers.waitUntil({ viewModel.getPeerIDForNickname("alice") != nil },
                                                     timeout: TestConstants.shortTimeout)
        #expect(didResolve)
        
        // Action: User types /msg command
        viewModel.sendMessage("/msg @alice Hello Private World")

        let didSend = await TestHelpers.waitUntil({ transport.sentPrivateMessages.count == 1 },
                                                  timeout: TestConstants.shortTimeout)
        #expect(didSend)
        
        // Assert:
        // 1. Should NOT go to public transport
        #expect(transport.sentMessages.isEmpty, "Command should not be sent as public message")
        
        // 2. Should go to private transport logic
        #expect(transport.sentPrivateMessages.count == 1)
        #expect(transport.sentPrivateMessages.first?.content == "Hello Private World")
        #expect(transport.sentPrivateMessages.first?.peerID == peerID)
    }

    @Test @MainActor
    func command_block_updatesIdentity() async throws {
        let (viewModel, transport, identity) = makePinnedViewModel()
        
        // Setup: Use simulateConnect
        let peerID = PeerID(str: "0000000000000002")
        // Mock the fingerprint so the block command finds it
        transport.peerFingerprints[peerID] = "fingerprint_123"
        transport.simulateConnect(peerID, nickname: "troll")

        let didResolve = await TestHelpers.waitUntil({ viewModel.getPeerIDForNickname("troll") != nil },
                                                     timeout: TestConstants.shortTimeout)
        #expect(didResolve)
        
        // Action
        viewModel.sendMessage("/block @troll")
        
        // Assert
        // Verify identity manager was called to block "fingerprint_123"
        let didBlock = await TestHelpers.waitUntil({ identity.isBlocked(fingerprint: "fingerprint_123") },
                                                   timeout: TestConstants.shortTimeout)
        #expect(didBlock)
    }

    // MARK: - Message Routing Logic

    @Test @MainActor
    func routing_incomingPrivateMessage_addsToPrivateChats() async {
        let (viewModel, _, _) = makePinnedViewModel()
        let senderID = PeerID(str: "sender_1")

        // Setup
        let message = BitchatMessage(
            id: "msg_1",
            sender: "bob",
            content: "Secret",
            timestamp: Date(),
            isRelay: false,
            originalSender: nil,
            isPrivate: true,
            recipientNickname: "me",
            senderPeerID: senderID,
            mentions: nil
        )

        // Action: Simulate incoming private message
        viewModel.didReceiveMessage(message)

        // Wait for async processing with proper timeout
        let found = await TestHelpers.waitUntil(
            { viewModel.privateChats[senderID]?.first?.content == "Secret" },
            timeout: TestConstants.defaultTimeout
        )

        // Assert
        #expect(found)
    }

    @Test @MainActor
    func routing_incomingPublicMessage_addsToPublicTimeline() async {
        let (viewModel, _, _) = makePinnedViewModel()
        let senderID = PeerID(str: "sender_2")

        // Action
        viewModel.didReceivePublicMessage(
            from: senderID,
            nickname: "charlie",
            content: "Public Hi",
            timestamp: Date(),
            messageID: "msg_2"
        )

        // Wait for async processing with proper timeout
        let found = await TestHelpers.waitUntil(
            {
                viewModel.publicMessages(for: .mesh).contains(where: { $0.content == "Public Hi" })
            },
            timeout: TestConstants.defaultTimeout
        )

        // Assert
        #expect(found)
    }
}
