-import Foundation
-import SourceKittenFramework
-
-public struct UnusedSetterValueRule: ConfigurationProviderRule {
+import SwiftSyntax
+
+public struct UnusedSetterValueRule: ConfigurationProviderRule, SwiftSyntaxRule {
     public var configuration = SeverityConfiguration(.warning)
 
     public init() {}

             get {
                 return Persister.shared.aValue
             }
             set() { }
            }
            """)
        ],
        triggeringExamples: [
            Example("""

        ]
    )

    public func makeVisitor(file: SwiftLintFile) -> ViolationsSyntaxVisitor? {
        Visitor(viewMode: .sourceAccurate)
    }
}

private extension UnusedSetterValueRule {
    final class Visitor: SyntaxVisitor, ViolationsSyntaxVisitor {
        private(set) var violationPositions: [AbsolutePosition] = []

        override func visitPost(_ node: AccessorDeclSyntax) {
            guard node.accessorKind.tokenKind == .contextualKeyword("set") else {
                return
            }

            let variableName = node.parameter?.name.withoutTrivia().text ?? "newValue"
            let visitor = NewValueUsageVisitor(variableName: variableName)
            if !visitor.walk(tree: node, handler: \.isVariableUsed) {
                if (Syntax(node).closestVariableOrSubscript()?.modifiers).containsOverride,
                    let body = node.body, body.statements.isEmpty {
                    return
                }

                violationPositions.append(node.positionAfterSkippingLeadingTrivia)
            }
        }

        override func visit(_ node: ProtocolDeclSyntax) -> SyntaxVisitorContinueKind {
            .skipChildren
        }
    }

    final class NewValueUsageVisitor: SyntaxVisitor {
        let variableName: String
        private(set) var isVariableUsed = false

        init(variableName: String) {
            self.variableName = variableName
            super.init(viewMode: .sourceAccurate)
        }

        override func visitPost(_ node: IdentifierExprSyntax) {
            if node.identifier.withoutTrivia().text == variableName {
                isVariableUsed = true
            }
        }
    }
}

private extension Syntax {
    func closestVariableOrSubscript() -> Either<SubscriptDeclSyntax, VariableDeclSyntax>? {
        if let subscriptDecl = self.as(SubscriptDeclSyntax.self) {
            return .left(subscriptDecl)
        } else if let variableDecl = self.as(VariableDeclSyntax.self) {
            return .right(variableDecl)
        }

        return parent?.closestVariableOrSubscript()
    }
}

private enum Either<L, R> {
    case left(L)
    case right(R)
}

private extension Either<SubscriptDeclSyntax, VariableDeclSyntax> {
    var modifiers: ModifierListSyntax? {
        switch self {
        case .left(let left):
            return left.modifiers
        case .right(let right):
            return right.modifiers
        }
    }
}

private extension ModifierListSyntax? {
    var containsOverride: Bool {
        self?.contains { elem in
            elem.name.tokenKind == .contextualKeyword("override")
        } ?? false
    }
}
