import XCTest
@testable import SherpaOnnxOfflineStt

/// Unit tests for StreamingStateManager
class StreamingStateManagerTests: XCTestCase {

    var manager: StreamingStateManager!

    override func setUp() {
        super.setUp()
        manager = StreamingStateManager()
    }

    override func tearDown() {
        manager = nil
        super.tearDown()
    }

    // MARK: - Initial State Tests

    func testInitialState() {
        let update = manager.updateState(newText: "", confidence: 0, audioLengthMs: 0, processingTimeMs: 0)
        XCTAssertTrue(update.volatile.isEmpty)
        XCTAssertTrue(update.confirmed.isEmpty)
        XCTAssertTrue(update.fullText.isEmpty)
    }

    func testDefaultThresholds() {
        XCTAssertEqual(manager.confirmationThreshold, STTConstants.defaultConfirmationThreshold)
        XCTAssertEqual(manager.minContextMs, STTConstants.defaultMinContextMs)
    }

    // MARK: - Update State Tests

    func testUpdateStateAddsVolatileText() {
        let update = manager.updateState(
            newText: "hello",
            confidence: 0.5,
            audioLengthMs: 100,
            processingTimeMs: 10
        )
        XCTAssertEqual(update.volatile, "hello")
        XCTAssertTrue(update.confirmed.isEmpty)
        XCTAssertEqual(update.fullText, "hello")
    }

    func testUpdateStateReplacesVolatileText() {
        _ = manager.updateState(newText: "hello", confidence: 0.5, audioLengthMs: 100, processingTimeMs: 10)
        let update = manager.updateState(newText: "hello world", confidence: 0.6, audioLengthMs: 100, processingTimeMs: 10)

        XCTAssertEqual(update.volatile, "hello world")
        XCTAssertTrue(update.confirmed.isEmpty)
    }

    func testUpdateStateConfirmsWithHighConfidence() {
        // Set thresholds for easier testing
        manager.confirmationThreshold = 0.8
        manager.minContextMs = 100

        // First update - accumulate context
        _ = manager.updateState(newText: "hello", confidence: 0.9, audioLengthMs: 50, processingTimeMs: 10)

        // Second update - still below minContext
        _ = manager.updateState(newText: "hello world", confidence: 0.9, audioLengthMs: 50, processingTimeMs: 10)

        // Third update - now context is 150ms > 100ms, should confirm
        let update = manager.updateState(newText: "new text", confidence: 0.9, audioLengthMs: 50, processingTimeMs: 10)

        // Previous "hello world" should be confirmed, "new text" is volatile
        XCTAssertEqual(update.volatile, "new text")
        XCTAssertEqual(update.confirmed, "hello world")
    }

    func testUpdateStateDoesNotConfirmWithLowConfidence() {
        manager.confirmationThreshold = 0.8
        manager.minContextMs = 100

        // Accumulate context with low confidence
        _ = manager.updateState(newText: "hello", confidence: 0.5, audioLengthMs: 200, processingTimeMs: 10)

        let update = manager.updateState(newText: "hello world", confidence: 0.5, audioLengthMs: 50, processingTimeMs: 10)

        // Should not confirm due to low confidence
        XCTAssertEqual(update.volatile, "hello world")
        XCTAssertTrue(update.confirmed.isEmpty)
    }

    // MARK: - Confirm Current Tests

    func testConfirmCurrentMovesVolatileToConfirmed() {
        _ = manager.updateState(newText: "hello world", confidence: 0.5, audioLengthMs: 100, processingTimeMs: 10)

        let update = manager.confirmCurrent(processingTimeMs: 20)

        XCTAssertTrue(update.volatile.isEmpty)
        XCTAssertEqual(update.confirmed, "hello world")
        XCTAssertEqual(update.fullText, "hello world")
        XCTAssertEqual(update.confidence, 1.0)
    }

    func testConfirmCurrentAppendsToExistingConfirmed() {
        manager.confirmationThreshold = 0.8
        manager.minContextMs = 0

        // First segment confirmed
        _ = manager.updateState(newText: "hello", confidence: 0.9, audioLengthMs: 100, processingTimeMs: 10)
        _ = manager.updateState(newText: "world", confidence: 0.9, audioLengthMs: 100, processingTimeMs: 10)

        // At this point "hello" is confirmed, "world" is volatile
        let update = manager.confirmCurrent(processingTimeMs: 10)

        XCTAssertTrue(update.volatile.isEmpty)
        XCTAssertEqual(update.confirmed, "hello world")
    }

    func testConfirmCurrentWithEmptyVolatile() {
        let update = manager.confirmCurrent(processingTimeMs: 10)

        XCTAssertTrue(update.volatile.isEmpty)
        XCTAssertTrue(update.confirmed.isEmpty)
    }

    // MARK: - Reset Tests

    func testReset() {
        _ = manager.updateState(newText: "hello", confidence: 0.5, audioLengthMs: 100, processingTimeMs: 10)
        manager.reset()

        let update = manager.updateState(newText: "", confidence: 0, audioLengthMs: 0, processingTimeMs: 0)
        XCTAssertTrue(update.volatile.isEmpty)
        XCTAssertTrue(update.confirmed.isEmpty)
        XCTAssertTrue(update.fullText.isEmpty)
    }

    // MARK: - Full Text Tests

    func testFullTextWithOnlyVolatile() {
        let update = manager.updateState(newText: "hello", confidence: 0.5, audioLengthMs: 100, processingTimeMs: 10)
        XCTAssertEqual(update.fullText, "hello")
    }

    func testFullTextWithOnlyConfirmed() {
        _ = manager.updateState(newText: "hello", confidence: 0.5, audioLengthMs: 100, processingTimeMs: 10)
        let update = manager.confirmCurrent(processingTimeMs: 10)
        XCTAssertEqual(update.fullText, "hello")
    }

    func testFullTextCombinesConfirmedAndVolatile() {
        manager.confirmationThreshold = 0.8
        manager.minContextMs = 50

        _ = manager.updateState(newText: "hello", confidence: 0.9, audioLengthMs: 100, processingTimeMs: 10)
        let update = manager.updateState(newText: "world", confidence: 0.9, audioLengthMs: 100, processingTimeMs: 10)

        // "hello" should be confirmed, "world" volatile
        XCTAssertEqual(update.fullText, "hello world")
    }

    func testFullTextTrimsWhitespace() {
        _ = manager.updateState(newText: "  hello  ", confidence: 0.5, audioLengthMs: 100, processingTimeMs: 10)
        let update = manager.confirmCurrent(processingTimeMs: 10)
        XCTAssertFalse(update.fullText.hasPrefix(" "))
        XCTAssertFalse(update.fullText.hasSuffix(" "))
    }

    // MARK: - Metrics Tests

    func testUpdateStateReturnsCorrectMetrics() {
        let update = manager.updateState(
            newText: "hello",
            confidence: 0.75,
            audioLengthMs: 500,
            processingTimeMs: 25
        )

        XCTAssertEqual(update.confidence, 0.75)
        XCTAssertEqual(update.processingTimeMs, 25)
        XCTAssertEqual(update.audioDurationMs, 500)
    }
}
