@testable import SwiftLintBuiltInRules
import TestHelpers
import XCTest

final class FileHeaderRuleTests: SwiftLintTestCase {
    private func validate(fileName: String, using configuration: Any) throws -> [StyleViolation] {
        let file = TestResources.path()
            .appending(path: "FileHeaderRuleFixtures", directoryHint: .isDirectory)
            .appending(path: fileName, directoryHint: .notDirectory)
        let rule = try FileHeaderRule(configuration: configuration)
        return rule.validate(file: SwiftLintFile(path: file)!)
    }

    func testFileHeaderWithDefaultConfiguration() {
        verifyRule(FileHeaderRule.description, skipCommentTests: true)
    }

    func testFileHeaderWithRequiredString() {
        let nonTriggeringExamples = [
            Example("// **Header"),
            Example("//\n// **Header"),
        ]
        let triggeringExamples = [
            Example("↓// Copyright\n"),
            Example("let foo = \"**Header\""),
            Example("let foo = 2 // **Header"),
            Example("let foo = 2\n// **Header"),
            Example("let foo = 2 // **Header"),
        ]
        let description = FileHeaderRule.description
            .with(nonTriggeringExamples: nonTriggeringExamples)
            .with(triggeringExamples: triggeringExamples)

        verifyRule(description, ruleConfiguration: ["required_string": "**Header"],
                   stringDoesntViolate: false, skipCommentTests: true,
                   skipDisableCommandTests: true, testMultiByteOffsets: false,
                   testShebang: false)
    }

    func testFileHeaderWithRequiredPattern() {
        let nonTriggeringExamples = [
            Example("// Copyright © 2016 Realm"),
            Example("//\n// Copyright © 2016 Realm)"),
        ]
        let triggeringExamples = [
            Example("↓// Copyright\n"),
            Example("↓// Copyright © foo Realm"),
            Example("↓// Copyright © 2016 MyCompany"),
        ]
        let description = FileHeaderRule.description
            .with(nonTriggeringExamples: nonTriggeringExamples)
            .with(triggeringExamples: triggeringExamples)

        verifyRule(description, ruleConfiguration: ["required_pattern": "\\d{4} Realm"],
                   stringDoesntViolate: false, skipCommentTests: true,
                   testMultiByteOffsets: false)
    }

    func testFileHeaderWithRequiredStringAndURLComment() {
        let nonTriggeringExamples = [
            Example("/* Check this url: https://github.com/realm/SwiftLint */")
        ]
        let triggeringExamples = [
            Example("/* Check this url: https://github.com/apple/swift */")
        ]
        let description = FileHeaderRule.description
            .with(nonTriggeringExamples: nonTriggeringExamples)
            .with(triggeringExamples: triggeringExamples)

        let config = ["required_string": "/* Check this url: https://github.com/realm/SwiftLint */"]
        verifyRule(description, ruleConfiguration: config,
                   stringDoesntViolate: false, skipCommentTests: true,
                   testMultiByteOffsets: false)
    }

    func testFileHeaderWithForbiddenString() {
        let nonTriggeringExamples = [
            Example("// Copyright\n"),
            Example("let foo = \"**All rights reserved.\""),
            Example("let foo = 2 // **All rights reserved."),
            Example("let foo = 2\n// **All rights reserved."),
            Example("let foo = 2 // **All rights reserved."),
        ]
        let triggeringExamples = [
            Example("// ↓**All rights reserved."),
            Example("//\n// ↓**All rights reserved."),
        ]
        let description = FileHeaderRule.description
            .with(nonTriggeringExamples: nonTriggeringExamples)
            .with(triggeringExamples: triggeringExamples)

        verifyRule(description, ruleConfiguration: ["forbidden_string": "**All rights reserved."],
                   skipCommentTests: true)
    }

    func testFileHeaderWithForbiddenPattern() {
        let nonTriggeringExamples = [
            Example("// Copyright\n"),
            Example("// FileHeaderRuleTests.m\n"),
            Example("let foo = \"FileHeaderRuleTests.swift\""),
            Example("let foo = 2 // FileHeaderRuleTests.swift."),
            Example("let foo = 2\n // FileHeaderRuleTests.swift."),
        ]
        let triggeringExamples = [
            Example("//↓ FileHeaderRuleTests.swift"),
            Example("//\n//↓ FileHeaderRuleTests.swift"),
        ]
        let description = FileHeaderRule.description
            .with(nonTriggeringExamples: nonTriggeringExamples)
            .with(triggeringExamples: triggeringExamples)

        verifyRule(description, ruleConfiguration: ["forbidden_pattern": "\\s\\w+\\.swift"],
                   skipCommentTests: true)
    }

    func testFileHeaderWithForbiddenPatternAndDocComment() {
        let nonTriggeringExamples = [
            Example("/// This is great tool with tests.\nclass GreatTool {}"),
            Example("class GreatTool {}"),
        ]
        let triggeringExamples = [
            Example("// FileHeaderRule↓Tests.swift"),
            Example("//\n// FileHeaderRule↓Tests.swift"),
        ]
        let description = FileHeaderRule.description
            .with(nonTriggeringExamples: nonTriggeringExamples)
            .with(triggeringExamples: triggeringExamples)

        verifyRule(description, ruleConfiguration: ["forbidden_pattern": "[tT]ests"],
                   skipCommentTests: true, testMultiByteOffsets: false)
    }

    func testFileHeaderWithRequiredStringUsingFilenamePlaceholder() {
        let configuration = ["required_string": "// SWIFTLINT_CURRENT_FILENAME"]

        // Non triggering tests
        XCTAssert(try validate(fileName: "FileNameMatchingSimple.swift", using: configuration).isEmpty)

        // Triggering tests
        XCTAssertEqual(try validate(fileName: "FileNameCaseMismatch.swift", using: configuration).count, 1)
        XCTAssertEqual(try validate(fileName: "FileNameMismatch.swift", using: configuration).count, 1)
        XCTAssertEqual(try validate(fileName: "FileNameMissing.swift", using: configuration).count, 1)
    }

    func testFileHeaderWithForbiddenStringUsingFilenamePlaceholder() {
        let configuration = ["forbidden_string": "// SWIFTLINT_CURRENT_FILENAME"]

        // Non triggering tests
        XCTAssert(try validate(fileName: "FileNameCaseMismatch.swift", using: configuration).isEmpty)
        XCTAssert(try validate(fileName: "FileNameMismatch.swift", using: configuration).isEmpty)
        XCTAssert(try validate(fileName: "FileNameMissing.swift", using: configuration).isEmpty)

        // Triggering tests
        XCTAssertEqual(try validate(fileName: "FileNameMatchingSimple.swift", using: configuration).count, 1)
    }

    func testFileHeaderWithRequiredPatternUsingFilenamePlaceholder() {
        let configuration1 = ["required_pattern": "// SWIFTLINT_CURRENT_FILENAME\n.*\\d{4}"]
        let configuration2 = [
            "required_pattern": "// Copyright © \\d{4}\n// File: \"SWIFTLINT_CURRENT_FILENAME\"",
        ]

        // Non triggering tests
        XCTAssert(try validate(fileName: "FileNameMatchingSimple.swift", using: configuration1).isEmpty)
        XCTAssert(try validate(fileName: "FileNameMatchingComplex.swift", using: configuration2).isEmpty)

        // Triggering tests
        XCTAssertEqual(try validate(fileName: "FileNameCaseMismatch.swift", using: configuration1).count, 1)
        XCTAssertEqual(try validate(fileName: "FileNameMismatch.swift", using: configuration1).count, 1)
        XCTAssertEqual(try validate(fileName: "FileNameMissing.swift", using: configuration1).count, 1)
    }

    func testFileHeaderWithForbiddenPatternUsingFilenamePlaceholder() {
        let configuration1 = ["forbidden_pattern": "// SWIFTLINT_CURRENT_FILENAME\n.*\\d{4}"]
        let configuration2 = ["forbidden_pattern": "//.*(\\s|\")SWIFTLINT_CURRENT_FILENAME(\\s|\").*"]

        // Non triggering tests
        XCTAssert(try validate(fileName: "FileNameCaseMismatch.swift", using: configuration1).isEmpty)
        XCTAssert(try validate(fileName: "FileNameMismatch.swift", using: configuration1).isEmpty)
        XCTAssert(try validate(fileName: "FileNameMissing.swift", using: configuration1).isEmpty)

        XCTAssert(try validate(fileName: "FileNameCaseMismatch.swift", using: configuration2).isEmpty)
        XCTAssert(try validate(fileName: "FileNameMismatch.swift", using: configuration2).isEmpty)
        XCTAssert(try validate(fileName: "FileNameMissing.swift", using: configuration2).isEmpty)

        // Triggering tests
        XCTAssertEqual(try validate(fileName: "FileNameMatchingSimple.swift", using: configuration1).count, 1)
        XCTAssertEqual(try validate(fileName: "FileNameMatchingComplex.swift", using: configuration2).count, 1)
    }

    func testFileHeaderShouldBeEmpty() {
        let configuration = ["forbidden_pattern": "."]

        // Non triggering tests
        XCTAssert(try validate(fileName: "FileHeaderEmpty.swift", using: configuration).isEmpty)
        XCTAssert(try validate(fileName: "DocumentedType.swift", using: configuration).isEmpty)

        // Triggering tests
        XCTAssertEqual(try validate(fileName: "FileNameCaseMismatch.swift", using: configuration).count, 1)
        XCTAssertEqual(try validate(fileName: "FileNameMismatch.swift", using: configuration).count, 1)
        XCTAssertEqual(try validate(fileName: "FileNameMissing.swift", using: configuration).count, 1)
    }

    func testSimplePattern() {
        let description = FileHeaderRule.description
            .with(nonTriggeringExamples: [
                Example("""
                    // Test

                    enum Test {}
                    """),
                Example("""
                    // Test
                    """),
                Example("""
                    // Test

                    """),
            ])
            .with(triggeringExamples: [])

        verifyRule(
            description,
            ruleConfiguration: [
                "required_pattern": #"""
                    \/\/ Test

                    """#, // The empty line at the end is important since YAML adds it as well in `|` blocks.
            ],
            skipCommentTests: true,
            testMultiByteOffsets: false
        )
    }

    func testPattern() {
        let description = FileHeaderRule.description
            .with(nonTriggeringExamples: [
                Example("""
                    //
                    //  Test.swift
                    //  Dummy App
                    //
                    //  Created by Alice Bob on 3.9.2025.
                    //  Copyright © 2025 Dummy Corporation. All rights reserved.
                    //

                    enum Test {}
                    """),
            ])
            .with(triggeringExamples: [])

        verifyRule(
            description,
            ruleConfiguration: [
                "required_pattern": #"""
                    \/\/
                    \/\/  Test\.swift
                    \/\/  .*?
                    \/\/
                    \/\/  Created by .*? on \d{1,2}[\.\/]\d{1,2}[\.\/]\d{2,4}\.
                    \/\/  Copyright © \d{4} Dummy Corporation\. All rights reserved\.
                    \/\/

                    """#, // The empty line at the end is important since YAML adds it as well in `|` blocks.
            ],
            skipCommentTests: true,
            testMultiByteOffsets: false
        )
    }
}
