//
//  ConfigurationFile.swift
//  
//
//  Created by Thibaut Schmitt on 30/08/2022.
//

import Foundation

struct ConfigurationFile: Codable, CustomDebugStringConvertible {
    var architecture: ConfigurationArchitecture?
    var analytics: [AnalyticsConfiguration]
    var colors: [ColorsConfiguration]
    var fonts: [FontsConfiguration]
    var images: [ImagesConfiguration]
    var strings: [StringsConfiguration]
    var tags: [TagsConfiguration]
    
    var runnableConfigurations: [Runnable] {
        let runnables: [[Runnable]] = [analytics, colors, fonts, images, strings, tags]
        return Array(runnables.joined())
    }
    
    var debugDescription: String {
        """
        \(analytics)
        -----------
        -----------
        \(colors)
        -----------
        -----------
        \(fonts)
        -----------
        -----------
        \(images)
        -----------
        -----------
        \(strings)
        -----------
        -----------
        \(tags)
        """
    }
}

struct ConfigurationArchitecture: Codable {
    let property: String
    let classname: String
    let path: String?
    let children: [ConfigurationArchitecture]?
    
    func getProperty(isStatic: Bool) -> String {
        "    \(isStatic ? "static " : "")let \(property) = \(classname)()"
    }
    
    func getClass(generateStaticProperty: Bool = true) -> String {
        guard children?.isEmpty == false else {
            return "class \(classname) {}"
        }
        
        let classDefinition = [
            "class \(classname) {",
            children?.map { $0.getProperty(isStatic: generateStaticProperty) }.joined(separator: "\n"),
            "}"
        ]
            .compactMap { $0 }
            .joined(separator: "\n")
        
        return [classDefinition, "", getSubclass()]
            .compactMap { $0 }
            .joined(separator: "\n")
    }
    
    func getSubclass() -> String? {
        guard let children else { return nil }
        return children.compactMap { arch in
            arch.getClass(generateStaticProperty: false)
        }
        .joined(separator: "\n\n")
    }
}

struct AnalyticsConfiguration: Codable, CustomDebugStringConvertible {
    let inputFile: String
    let target: String
    let extensionOutputPath: String
    let extensionName: String?
    let extensionSuffix: String?
    private let staticMembers: Bool?
    
    var staticMembersOptions: Bool {
        if let staticMembers = staticMembers {
            return staticMembers
        }
        return false
    }
    
    internal init(inputFile: String,
                  target: String,
                  extensionOutputPath: String,
                  extensionName: String?,
                  extensionSuffix: String?,
                  staticMembers: Bool?) {
        self.inputFile = inputFile
        self.target = target
        self.extensionOutputPath = extensionOutputPath
        self.extensionName = extensionName
        self.extensionSuffix = extensionSuffix
        self.staticMembers = staticMembers
    }
    
    var debugDescription: String {
        """
        Analytics configuration:
            - Input file: \(inputFile)
            - Target: \(target)
            - Extension output path: \(extensionOutputPath)
            - Extension name: \(extensionName ?? "-")
            - Extension suffix: \(extensionSuffix ?? "-")
        """
    }
}

struct ColorsConfiguration: Codable, CustomDebugStringConvertible {
    let inputFile: String
    let style: String
    let xcassetsPath: String
    let extensionOutputPath: String
    let extensionName: String?
    let extensionNameUIKit: String?
    let extensionSuffix: String?
    private let staticMembers: Bool?
    
    var staticMembersOptions: Bool {
        if let staticMembers = staticMembers {
            return staticMembers
        }
        return false
    }
    
    internal init(inputFile: String,
                  style: String,
                  xcassetsPath: String,
                  extensionOutputPath: String,
                  extensionName: String?,
                  extensionNameUIKit: String?,
                  extensionSuffix: String?,
                  staticMembers: Bool?) {
        self.inputFile = inputFile
        self.style = style
        self.xcassetsPath = xcassetsPath
        self.extensionOutputPath = extensionOutputPath
        self.extensionName = extensionName
        self.extensionNameUIKit = extensionNameUIKit
        self.extensionSuffix = extensionSuffix
        self.staticMembers = staticMembers
    }
    
    var debugDescription: String {
        """
        Colors configuration:
            - Input file: \(inputFile)
            - Style: \(style)
            - Xcassets path: \(xcassetsPath)
            - Extension output path: \(extensionOutputPath)
            - Extension name: \(extensionName ?? "-")
            - Extension name UIKit: \(extensionNameUIKit ?? "-")
            - Extension suffix: \(extensionSuffix ?? "-")
        """
    }
}

struct FontsConfiguration: Codable, CustomDebugStringConvertible {
    let inputFile: String
    let extensionOutputPath: String
    let extensionName: String?
    let extensionNameUIKit: String?
    let extensionSuffix: String?
    let infoPlistPaths: String?
    private let staticMembers: Bool?
    
    var staticMembersOptions: Bool {
        if let staticMembers = staticMembers {
            return staticMembers
        }
        return false
    }
    
    internal init(inputFile: String,
                  extensionOutputPath: String,
                  extensionName: String?,
                  extensionNameUIKit: String?,
                  extensionSuffix: String?,
                  infoPlistPaths: String?,
                  staticMembers: Bool?) {
        self.inputFile = inputFile
        self.extensionOutputPath = extensionOutputPath
        self.extensionName = extensionName
        self.extensionNameUIKit = extensionNameUIKit
        self.extensionSuffix = extensionSuffix
        self.infoPlistPaths = infoPlistPaths
        self.staticMembers = staticMembers
    }
    
    var debugDescription: String {
        """
        Fonts configuration:
            - Input file: \(inputFile)
            - Extension output path: \(extensionOutputPath)
            - Extension name: \(extensionName ?? "-")
            - Extension name UIKit: \(extensionNameUIKit ?? "-")
            - Extension suffix: \(extensionSuffix ?? "-")
            - InfoPlistPaths: \(infoPlistPaths ?? "-")
        """
    }
}

struct ImagesConfiguration: Codable, CustomDebugStringConvertible {
    let inputFile: String
    let xcassetsPath: String
    let extensionOutputPath: String
    let extensionName: String?
    let extensionNameUIKit: String?
    let extensionSuffix: String?
    private let staticMembers: Bool?
    
    var staticMembersOptions: Bool {
        if let staticMembers = staticMembers {
            return staticMembers
        }
        return false
    }
    
    internal init(inputFile: String,
                  xcassetsPath: String,
                  extensionOutputPath: String,
                  extensionName: String?,
                  extensionNameUIKit: String?,
                  extensionSuffix: String?,
                  staticMembers: Bool?) {
        self.inputFile = inputFile
        self.xcassetsPath = xcassetsPath
        self.extensionOutputPath = extensionOutputPath
        self.extensionName = extensionName
        self.extensionNameUIKit = extensionNameUIKit
        self.extensionSuffix = extensionSuffix
        self.staticMembers = staticMembers
    }
    
    var debugDescription: String {
        """
        Images configuration:
            - Input file: \(inputFile)
            - Xcassets path: \(xcassetsPath)
            - Extension output path: \(extensionOutputPath)
            - Extension name: \(extensionName ?? "-")
            - Extension name UIKit: \(extensionNameUIKit ?? "-")
            - Extension suffix: \(extensionSuffix ?? "-")
        """
    }
}

struct StringsConfiguration: Codable, CustomDebugStringConvertible {
    let inputFile: String
    let outputPath: String
    let langs: String
    let defaultLang: String
    let extensionOutputPath: String
    let extensionName: String?
    let extensionSuffix: String?
    private let staticMembers: Bool?
    
    var staticMembersOptions: Bool {
        if let staticMembers = staticMembers {
            return staticMembers
        }
        return false
    }
    
    internal init(inputFile: String,
                  outputPath: String,
                  langs: String,
                  defaultLang: String,
                  extensionOutputPath: String,
                  extensionName: String?,
                  extensionSuffix: String?,
                  staticMembers: Bool?) {
        self.inputFile = inputFile
        self.outputPath = outputPath
        self.langs = langs
        self.defaultLang = defaultLang
        self.extensionOutputPath = extensionOutputPath
        self.extensionName = extensionName
        self.extensionSuffix = extensionSuffix
        self.staticMembers = staticMembers
    }
    
    var debugDescription: String {
        """
        Strings configuration:
            - Input file: \(inputFile)
            - Output path: \(outputPath)
            - Langs: \(langs)
            - Default lang: \(defaultLang)
            - Extension output path: \(extensionOutputPath)
            - Extension name: \(extensionName ?? "-")
            - Extension suffix: \(extensionSuffix ?? "-")
        """
    }
}

struct TagsConfiguration: Codable, CustomDebugStringConvertible {
    let inputFile: String
    let lang: String
    let extensionOutputPath: String
    let extensionName: String?
    let extensionSuffix: String?
    private let staticMembers: Bool?
    
    var staticMembersOptions: Bool {
        if let staticMembers = staticMembers {
            return staticMembers
        }
        return false
    }
    
    internal init(inputFile: String,
                  lang: String,
                  extensionOutputPath: String,
                  extensionName: String?,
                  extensionSuffix: String?,
                  staticMembers: Bool?) {
        self.inputFile = inputFile
        self.lang = lang
        self.extensionOutputPath = extensionOutputPath
        self.extensionName = extensionName
        self.extensionSuffix = extensionSuffix
        self.staticMembers = staticMembers
    }
    
    var debugDescription: String {
        """
        Tags configuration:
            - Input file: \(inputFile)
            - Lang: \(lang)
            - Extension output path: \(extensionOutputPath)
            - Extension name: \(extensionName ?? "-")
            - Extension suffix: \(extensionSuffix ?? "-")
        """
    }
}
