import SwiftLintCore
import SwiftSyntax

@SwiftSyntaxRule(explicitRewriter: true)
struct EmptyParametersRule: Rule {
    var configuration = SeverityConfiguration<Self>(.warning)

    static let description = RuleDescription(
        identifier: "empty_parameters",
        name: "Empty Parameters",
        description: "Prefer `() -> ` over `Void -> `",
        kind: .style,
        nonTriggeringExamples: #examples([
            "let abc: () -> Void = {}",
            "func foo(completion: () -> Void)",
            "func foo(completion: () throws -> Void)",
            "let foo: (ConfigurationTests) -> Void throws -> Void)",
            "let foo: (ConfigurationTests) ->   Void throws -> Void)",
            "let foo: (ConfigurationTests) ->Void throws -> Void)",
        ]),
        triggeringExamples: #examples([
            "let abc: ↓(Void) -> Void = {}",
            "func foo(completion: ↓(Void) -> Void)",
            "func foo(completion: ↓(Void) throws -> Void)",
            "let foo: ↓(Void) -> () throws -> Void)",
        ]),
        corrections: #corrections([
            "let abc: ↓(Void) -> Void = {}": "let abc: () -> Void = {}",
            "func foo(completion: ↓(Void) -> Void)": "func foo(completion: () -> Void)",
            "func foo(completion: ↓(Void) throws -> Void)":
                "func foo(completion: () throws -> Void)",
            "let foo: ↓(Void) -> () throws -> Void)": "let foo: () -> () throws -> Void)",
        ])
    )
}

private extension EmptyParametersRule {
    final class Visitor: ViolationsSyntaxVisitor<ConfigurationType> {
        override func visitPost(_ node: FunctionTypeSyntax) {
            guard let violationPosition = node.emptyParametersViolationPosition else {
                return
            }

            violations.append(violationPosition)
        }
    }

    final class Rewriter: ViolationsSyntaxRewriter<ConfigurationType> {
        override func visit(_ node: FunctionTypeSyntax) -> TypeSyntax {
            guard node.emptyParametersViolationPosition != nil else {
                return super.visit(node)
            }
            numberOfCorrections += 1
            return super.visit(node.with(\.parameters, TupleTypeElementListSyntax([])))
        }
    }
}

private extension FunctionTypeSyntax {
    var emptyParametersViolationPosition: AbsolutePosition? {
        guard
            let argument = parameters.onlyElement,
            leftParen.presence == .present,
            rightParen.presence == .present,
            let simpleType = argument.type.as(IdentifierTypeSyntax.self),
            simpleType.typeName == "Void"
        else {
            return nil
        }

        return leftParen.positionAfterSkippingLeadingTrivia
    }
}
