import PackagePlugin

protocol CommandContext {
    var tool: String { get throws }

    var cacheDirectory: String { get }

    var workingDirectory: String { get }

    var unitName: String { get }

    var subUnitName: String { get }

    func targets(named names: [String]) throws -> [(paths: [String], name: String)]
}

extension PluginContext: CommandContext {
    var tool: String {
        get throws {
            try tool(named: "swiftlint").path.string
        }
    }

    var cacheDirectory: String {
        pluginWorkDirectory.string
    }

    var workingDirectory: String {
        package.directory.string
    }

    var unitName: String {
        "package"
    }

    var subUnitName: String {
        "module"
    }

    func targets(named names: [String]) throws -> [(paths: [String], name: String)] {
        let targets = names.isEmpty
            ? package.targets
            : try package.targets(named: names)
        return targets.compactMap { target in
            guard let target = target.sourceModule else {
                Diagnostics.warning("Target '\(target.name)' is not a source module; skipping it")
                return nil
            }
            // Packages have a 1-to-1 mapping between targets and directories.
            return (paths: [target.directory.string], name: target.name)
        }
    }
}

#if canImport(XcodeProjectPlugin)

import XcodeProjectPlugin

extension XcodePluginContext: CommandContext {
    var tool: String {
        get throws {
            try tool(named: "swiftlint").path.string
        }
    }

    var cacheDirectory: String {
        pluginWorkDirectory.string
    }

    var workingDirectory: String {
        xcodeProject.directory.string
    }

    var unitName: String {
        "project"
    }

    var subUnitName: String {
        "target"
    }

    func targets(named names: [String]) -> [(paths: [String], name: String)] {
        if names.isEmpty {
            return [(paths: [xcodeProject.directory.string], name: xcodeProject.displayName)]
        }
        return xcodeProject.targets
            .filter { names.contains($0.displayName) }
            .map { (paths: $0.inputFiles.map(\.path.string).filter { $0.hasSuffix(".swift") }, name: $0.displayName) }
    }
}

#endif
