import Foundation
import JSONUtilities
import PathKit

public struct TargetSource: Equatable {
    public static let optionalDefault = false
    
    public var path: String {
        didSet {
            path = (path as NSString).standardizingPath
        }
    }
    
    public var name: String?
    public var group: String?
    public var compilerFlags: [String]
    public var excludes: [String]
    public var includes: [String]
    public var explicitFolders: [String]
    public var type: SourceType?
    public var optional: Bool
    public var buildPhase: BuildPhaseSpec?
    public var headerVisibility: HeaderVisibility?
    public var createIntermediateGroups: Bool?
    public var attributes: [String]
    public var resourceTags: [String]
    public var inferDestinationFiltersByPath: Bool?
    public var destinationFilters: [SupportedDestination]?

    public enum HeaderVisibility: String {
        case `public`
        case `private`
        case project

        public var settingName: String {
            switch self {
            case .public: return "Public"
            case .private: return "Private"
            case .project: return "Project"
            }
        }
    }

    public init(
        path: String,
        name: String? = nil,
        group: String? = nil,
        compilerFlags: [String] = [],
        excludes: [String] = [],
        includes: [String] = [],
        explicitFolders: [String] = [],
        type: SourceType? = nil,
        optional: Bool = optionalDefault,
        buildPhase: BuildPhaseSpec? = nil,
        headerVisibility: HeaderVisibility? = nil,
        createIntermediateGroups: Bool? = nil,
        attributes: [String] = [],
        resourceTags: [String] = [],
        inferDestinationFiltersByPath: Bool? = nil,
        destinationFilters: [SupportedDestination]? = nil
    ) {
        self.path = (path as NSString).standardizingPath
        self.name = name
        self.group = group
        self.compilerFlags = compilerFlags
        self.excludes = excludes
        self.includes = includes
        self.explicitFolders = explicitFolders
        self.type = type
        self.optional = optional
        self.buildPhase = buildPhase
        self.headerVisibility = headerVisibility
        self.createIntermediateGroups = createIntermediateGroups
        self.attributes = attributes
        self.resourceTags = resourceTags
        self.inferDestinationFiltersByPath = inferDestinationFiltersByPath
        self.destinationFilters = destinationFilters
    }
}

extension TargetSource: ExpressibleByStringLiteral {

    public init(stringLiteral value: String) {
        self = TargetSource(path: value)
    }

    public init(extendedGraphemeClusterLiteral value: String) {
        self = TargetSource(path: value)
    }

    public init(unicodeScalarLiteral value: String) {
        self = TargetSource(path: value)
    }
}

extension TargetSource: JSONObjectConvertible {

    public init(jsonDictionary: JSONDictionary) throws {
        path = try jsonDictionary.json(atKeyPath: "path")
        path = (path as NSString).standardizingPath // Done in two steps as the compiler can't figure out the types otherwise
        name = jsonDictionary.json(atKeyPath: "name")
        group = jsonDictionary.json(atKeyPath: "group")

        let maybeCompilerFlagsString: String? = jsonDictionary.json(atKeyPath: "compilerFlags")
        let maybeCompilerFlagsArray: [String]? = jsonDictionary.json(atKeyPath: "compilerFlags")
        compilerFlags = maybeCompilerFlagsArray ??
            maybeCompilerFlagsString.map { $0.split(separator: " ").map { String($0) } } ?? []

        headerVisibility = jsonDictionary.json(atKeyPath: "headerVisibility")
        excludes = jsonDictionary.json(atKeyPath: "excludes") ?? []
        includes = jsonDictionary.json(atKeyPath: "includes") ?? []
        explicitFolders = jsonDictionary.json(atKeyPath: "explicitFolders") ?? []
        type = jsonDictionary.json(atKeyPath: "type")
        optional = jsonDictionary.json(atKeyPath: "optional") ?? TargetSource.optionalDefault

        if let string: String = jsonDictionary.json(atKeyPath: "buildPhase") {
            buildPhase = try BuildPhaseSpec(string: string)
        } else if let dict: JSONDictionary = jsonDictionary.json(atKeyPath: "buildPhase") {
            buildPhase = try BuildPhaseSpec(jsonDictionary: dict)
        }

        createIntermediateGroups = jsonDictionary.json(atKeyPath: "createIntermediateGroups")
        attributes = jsonDictionary.json(atKeyPath: "attributes") ?? []
        resourceTags = jsonDictionary.json(atKeyPath: "resourceTags") ?? []
        
        inferDestinationFiltersByPath = jsonDictionary.json(atKeyPath: "inferDestinationFiltersByPath")
        
        if let destinationFilters: [SupportedDestination] = jsonDictionary.json(atKeyPath: "destinationFilters") {
            self.destinationFilters = destinationFilters
        }
    }
}

extension TargetSource: JSONEncodable {
    public func toJSONValue() -> Any {
        var dict: [String: Any?] = [
            "compilerFlags": compilerFlags,
            "excludes": excludes,
            "includes": includes,
            "explicitFolders": explicitFolders,
            "name": name,
            "group": group,
            "headerVisibility": headerVisibility?.rawValue,
            "type": type?.rawValue,
            "buildPhase": buildPhase?.toJSONValue(),
            "createIntermediateGroups": createIntermediateGroups,
            "resourceTags": resourceTags,
            "path": path,
            "inferDestinationFiltersByPath": inferDestinationFiltersByPath,
            "destinationFilters": destinationFilters?.map { $0.rawValue },
        ]

        if optional != TargetSource.optionalDefault {
            dict["optional"] = optional
        }

        return dict
    }
}

extension TargetSource: PathContainer {

    static var pathProperties: [PathProperty] {
        [
            .string("path"),
        ]
    }
}
