import XCTest
@testable import SaplingEditor

final class EditorDirtyLineInvalidationTests: XCTestCase {
    func testInitialRenderTouchesEveryLine() {
        let source = "One\nTwo\nThree"

        let plan = EditorDirtyLineInvalidator.plan(
            previousText: nil,
            currentText: source,
            previousActiveLineIndex: nil,
            currentActiveLineIndex: 0
        )

        XCTAssertEqual(plan.reason, .initial)
        XCTAssertTrue(plan.isFullRender)
        XCTAssertEqual(plan.dirtyLineIndexes, [0, 1, 2])
    }

    func testActiveLineChangeTouchesOnlyPreviousAndCurrentActiveLines() {
        let source = "One\nTwo\nThree\nFour"

        let plan = EditorDirtyLineInvalidator.plan(
            previousText: source,
            currentText: source,
            previousActiveLineIndex: 1,
            currentActiveLineIndex: 3
        )

        XCTAssertEqual(plan.reason, .activeLineChange)
        XCTAssertFalse(plan.isFullRender)
        XCTAssertEqual(plan.dirtyLineIndexes, [1, 3])
    }

    func testSingleLineEditTouchesChangedLineAndNeighbors() {
        let previous = "One\nTwo\nThree\nFour\nFive"
        let current = "One\nTwo\nThree updated\nFour\nFive"

        let plan = EditorDirtyLineInvalidator.plan(
            previousText: previous,
            currentText: current,
            previousActiveLineIndex: 2,
            currentActiveLineIndex: 2
        )

        XCTAssertEqual(plan.reason, .sourceChange)
        XCTAssertFalse(plan.isFullRender)
        XCTAssertEqual(plan.dirtyLineIndexes, [1, 2, 3])
    }

    func testLineBreakInsertionTouchesSplitLineAndNeighbors() {
        let previous = "One\nTwo three\nFour"
        let current = "One\nTwo\nthree\nFour"

        let plan = EditorDirtyLineInvalidator.plan(
            previousText: previous,
            currentText: current,
            previousActiveLineIndex: 1,
            currentActiveLineIndex: 2
        )

        XCTAssertEqual(plan.reason, .sourceChange)
        XCTAssertFalse(plan.isFullRender)
        XCTAssertEqual(plan.dirtyLineIndexes, [0, 1, 2])
    }

    func testUnchangedViewUpdateDoesNotRequireStyling() {
        let source = "One\nTwo"

        let plan = EditorDirtyLineInvalidator.plan(
            previousText: source,
            currentText: source,
            previousActiveLineIndex: 1,
            currentActiveLineIndex: 1
        )

        XCTAssertEqual(plan.reason, .viewUpdate)
        XCTAssertFalse(plan.requiresStyling)
        XCTAssertTrue(plan.dirtyLineIndexes.isEmpty)
    }
}
