import Foundation
import SwiftSyntax

// MARK: - CommandVisitor

/// Visits the source syntax tree to collect all SwiftLint-style comment commands.
final class CommandVisitor: SyntaxVisitor {
    private(set) var commands: [Command] = []
    let locationConverter: SourceLocationConverter

    init(locationConverter: SourceLocationConverter) {
        self.locationConverter = locationConverter
        super.init(viewMode: .sourceAccurate)
    }

    override func visitPost(_ node: TokenSyntax) {
        collectCommands(in: node.leadingTrivia, offset: node.position)
        collectCommands(in: node.trailingTrivia, offset: node.endPositionBeforeTrailingTrivia)
    }

    private func collectCommands(in trivia: Trivia, offset: AbsolutePosition) {
        var position = offset
        for piece in trivia {
            switch piece {
            case .lineComment(let comment):
                guard let lower = comment.range(of: "swiftlint:")?.lowerBound.samePosition(in: comment.utf8) else {
                    break
                }
                let offset = comment.utf8.distance(from: comment.utf8.startIndex, to: lower)
                let location = locationConverter.location(for: position.advanced(by: offset))
                let line = locationConverter.sourceLines[location.line - 1]
                guard let character = line.characterPosition(of: location.column) else {
                    break
                }
                let command = Command(
                    commandString: String(comment[lower...]),
                    line: location.line,
                    range: character..<(character + piece.sourceLength.utf8Length - offset)
                )
                commands.append(command)
            default:
                break
            }
            position += piece.sourceLength
        }
    }
}
