// AUTO-GENERATED by scripts/gen-swift-fixer.ts from src/core/swift-fix-rules.ts.
// Do not edit by hand — run 'npm run gen:swift-fixer' to regenerate.
//
// The Xcode Source Editor Extension is sandboxed and can't shell out to
// the axint CLI, so we ship a Swift-native implementation of the fix
// rules. Both this file and the TypeScript fixer read from the same
// registry, so they stay in lockstep.

import Foundation

struct AxintFix: Equatable {
    let code: String
    let message: String
}

struct AxintFixResult {
    let source: String
    let applied: [AxintFix]
}

enum AxintFixer {
    static func fix(_ source: String) -> AxintFixResult {
        var current = source
        var applied: [AxintFix] = []

        // AX703 — @State let → @State var
        do {
            let (next, count) = applyRegex(current, pattern: "(@State\\b[^=\\n]*?)\\blet(\\s+\\w+)", template: "$1var$2")
            if count > 0 {
                current = next
                applied.append(.init(code: "AX703", message: "@State let → @State var"))
            }
        }

        // AX708 — @Binding let → @Binding var
        do {
            let (next, count) = applyRegex(current, pattern: "(@Binding\\b[^=\\n]*?)\\blet(\\s+\\w+)", template: "$1var$2")
            if count > 0 {
                current = next
                applied.append(.init(code: "AX708", message: "@Binding let → @Binding var"))
            }
        }

        // AX709 — @ObservedObject let → @ObservedObject var
        do {
            let (next, count) = applyRegex(current, pattern: "(@ObservedObject\\b[^=\\n]*?)\\blet(\\s+\\w+)", template: "$1var$2")
            if count > 0 {
                current = next
                applied.append(.init(code: "AX709", message: "@ObservedObject let → @ObservedObject var"))
            }
        }

        // AX710 — @StateObject let → @StateObject var
        do {
            let (next, count) = applyRegex(current, pattern: "(@StateObject\\b[^=\\n]*?)\\blet(\\s+\\w+)", template: "$1var$2")
            if count > 0 {
                current = next
                applied.append(.init(code: "AX710", message: "@StateObject let → @StateObject var"))
            }
        }

        // AX711 — @EnvironmentObject let → @EnvironmentObject var
        do {
            let (next, count) = applyRegex(current, pattern: "(@EnvironmentObject\\b[^=\\n]*?)\\blet(\\s+\\w+)", template: "$1var$2")
            if count > 0 {
                current = next
                applied.append(.init(code: "AX711", message: "@EnvironmentObject let → @EnvironmentObject var"))
            }
        }

        // AX704 — AppIntent static let title → static var title
        do {
            let (next, count) = applyRegex(current, pattern: "\\bstatic\\s+let\\s+title\\s*:\\s*LocalizedStringResource\\b", template: "static var title: LocalizedStringResource")
            if count > 0 {
                current = next
                applied.append(.init(code: "AX704", message: "AppIntent static let title → static var title"))
            }
        }

        // AX720 — DispatchQueue.main.async → Task { @MainActor in }
        do {
            let (next, count) = applyRegex(current, pattern: "\\bDispatchQueue\\.main\\.async\\s*(?:\\(\\s*execute\\s*:\\s*)?\\{", template: "Task { @MainActor in")
            if count > 0 {
                current = next
                applied.append(.init(code: "AX720", message: "DispatchQueue.main.async → Task { @MainActor in }"))
            }
        }

        // AX727 — nonisolated var → nonisolated let
        do {
            let (next, count) = applyRegex(current, pattern: "\\bnonisolated\\s+var\\s+(\\w+)", template: "nonisolated let $1")
            if count > 0 {
                current = next
                applied.append(.init(code: "AX727", message: "nonisolated var → nonisolated let"))
            }
        }

        // AX725 — lazy var in actor → var
        do {
            let (next, count) = applyRegex(current, pattern: "\\blazy\\s+var\\b", template: "var")
            if count > 0 {
                current = next
                applied.append(.init(code: "AX725", message: "lazy var in actor → var"))
            }
        }

        // AX726 — Task.detached → Task
        do {
            let (next, count) = applyRegex(current, pattern: "\\bTask\\.detached\\s*\\{", template: "Task {")
            if count > 0 {
                current = next
                applied.append(.init(code: "AX726", message: "Task.detached → Task"))
            }
        }

        // AX734 — DispatchQueue.global().async → Task.detached
        do {
            let (next, count) = applyRegex(current, pattern: "\\bDispatchQueue\\.global\\([^)]*\\)\\.async\\s*(?:\\(\\s*execute\\s*:\\s*)?\\{", template: "Task.detached {")
            if count > 0 {
                current = next
                applied.append(.init(code: "AX734", message: "DispatchQueue.global().async → Task.detached"))
            }
        }

        // AX701 — inject perform() stub into AppIntent
        if let next = injectIntoStruct(
            current,
            conformance: "AppIntent",
            sentinel: "func perform(",
            stub: #"""
    func perform() async throws -> some IntentResult {
        return .result()
    }
"""#
        ) {
            current = next
            applied.append(.init(code: "AX701", message: "inject perform() stub into AppIntent"))
        }

        // AX702 — inject body into Widget
        if let next = injectIntoStruct(
            current,
            conformance: "Widget",
            sentinel: "var body",
            stub: #"""
    var body: some WidgetConfiguration {
        EmptyWidgetConfiguration()
    }
"""#
        ) {
            current = next
            applied.append(.init(code: "AX702", message: "inject body into Widget"))
        }

        // AX704 — inject static title into AppIntent
        if let next = injectIntoStruct(
            current,
            conformance: "AppIntent",
            sentinel: "static var title",
            stub: #"""
    static var title: LocalizedStringResource = "Intent"
"""#
        ) {
            current = next
            applied.append(.init(code: "AX704", message: "inject static title into AppIntent"))
        }

        // AX705 — inject placeholder(in:) into TimelineProvider
        if let next = injectIntoStruct(
            current,
            conformance: "TimelineProvider",
            sentinel: "func placeholder(",
            stub: #"""
    func placeholder(in context: Context) -> Entry {
        fatalError("TODO: return placeholder entry")
    }
"""#
        ) {
            current = next
            applied.append(.init(code: "AX705", message: "inject placeholder(in:) into TimelineProvider"))
        }

        // AX706 — inject getSnapshot(in:completion:) into TimelineProvider
        if let next = injectIntoStruct(
            current,
            conformance: "TimelineProvider",
            sentinel: "func getSnapshot(",
            stub: #"""
    func getSnapshot(in context: Context, completion: @escaping (Entry) -> Void) {
        completion(placeholder(in: context))
    }
"""#
        ) {
            current = next
            applied.append(.init(code: "AX706", message: "inject getSnapshot(in:completion:) into TimelineProvider"))
        }

        // AX707 — inject getTimeline(in:completion:) into TimelineProvider
        if let next = injectIntoStruct(
            current,
            conformance: "TimelineProvider",
            sentinel: "func getTimeline(",
            stub: #"""
    func getTimeline(in context: Context, completion: @escaping (Timeline<Entry>) -> Void) {
        completion(Timeline(entries: [placeholder(in: context)], policy: .never))
    }
"""#
        ) {
            current = next
            applied.append(.init(code: "AX707", message: "inject getTimeline(in:completion:) into TimelineProvider"))
        }

        // AX713 — inject let date: Date into TimelineEntry
        if let next = injectIntoStruct(
            current,
            conformance: "TimelineEntry",
            sentinel: "let date",
            stub: #"""
    let date: Date
"""#
        ) {
            current = next
            applied.append(.init(code: "AX713", message: "inject let date: Date into TimelineEntry"))
        }

        // AX714 — inject Scene body into App
        if let next = injectIntoStruct(
            current,
            conformance: "App",
            sentinel: "var body",
            stub: #"""
    var body: some Scene {
        WindowGroup { ContentView() }
    }
"""#
        ) {
            current = next
            applied.append(.init(code: "AX714", message: "inject Scene body into App"))
        }

        // AX712 — inject appShortcuts into AppShortcutsProvider
        if let next = injectIntoStruct(
            current,
            conformance: "AppShortcutsProvider",
            sentinel: "static var appShortcuts",
            stub: #"""
    static var appShortcuts: [AppShortcut] {
        []
    }
"""#
        ) {
            current = next
            applied.append(.init(code: "AX712", message: "inject appShortcuts into AppShortcutsProvider"))
        }

        // AX740 — inject ContentState into ActivityAttributes
        if let next = injectIntoStruct(
            current,
            conformance: "ActivityAttributes",
            sentinel: "ContentState",
            stub: #"""
    struct ContentState: Codable, Hashable {
        var progress: Double
    }
"""#
        ) {
            current = next
            applied.append(.init(code: "AX740", message: "inject ContentState into ActivityAttributes"))
        }

        return AxintFixResult(source: current, applied: applied)
    }

    // MARK: - Regex helper

    private static func applyRegex(
        _ source: String,
        pattern: String,
        template: String
    ) -> (String, Int) {
        guard let regex = try? NSRegularExpression(pattern: pattern, options: []) else {
            return (source, 0)
        }
        let range = NSRange(source.startIndex..., in: source)
        let matches = regex.matches(in: source, options: [], range: range)
        if matches.isEmpty { return (source, 0) }
        let rewritten = regex.stringByReplacingMatches(
            in: source,
            options: [],
            range: range,
            withTemplate: template
        )
        return (rewritten, matches.count)
    }

    // MARK: - Struct body injection

    private static func injectIntoStruct(
        _ source: String,
        conformance: String,
        sentinel: String,
        stub: String
    ) -> String? {
        let escaped = NSRegularExpression.escapedPattern(for: conformance)
        let pattern = "\\bstruct\\s+(\\w+)\\s*:\\s*[^{]*\\b\(escaped)\\b[^{]*\\{"
        guard let regex = try? NSRegularExpression(pattern: pattern, options: []) else {
            return nil
        }
        let nsRange = NSRange(source.startIndex..., in: source)
        let matches = regex.matches(in: source, options: [], range: nsRange)

        for match in matches {
            guard let declRange = Range(match.range, in: source) else { continue }
            guard let openBrace = source.range(
                of: "{",
                range: declRange.upperBound..<source.endIndex
            ) else { continue }
            guard let closeBrace = matchingCloseBrace(in: source, openAt: openBrace.lowerBound) else {
                continue
            }
            let body = source[openBrace.upperBound..<closeBrace]
            if body.contains(sentinel) { continue }

            var result = source
            result.insert(contentsOf: "\n" + stub, at: openBrace.upperBound)
            return result
        }
        return nil
    }

    private static func matchingCloseBrace(in source: String, openAt: String.Index) -> String.Index? {
        var depth = 0
        var idx = openAt
        while idx < source.endIndex {
            let c = source[idx]
            if c == "{" { depth += 1 }
            if c == "}" {
                depth -= 1
                if depth == 0 { return idx }
            }
            idx = source.index(after: idx)
        }
        return nil
    }
}
