import Foundation
import ComplyCubeMobileSDK

public protocol StageListParsing {
    func parseStages(from rawStages: [Any]) throws -> [ComplyCubeMobileSDKStage]
}

public protocol LookAndFeelParsing {
    func parseSDK(from raw: [String: Any]) throws -> LookAndFeel
}
public protocol ColorSchemeParsing {
    func parseSDK(from raw: [String: Any]) throws -> ComplyCubeColourScheme
}
public protocol CountryExtracting {
    func extract(from raw: Any) throws -> [Country]
    func getAllExtractedCountries() -> [Country]
}

public extension CountryExtracting {
    func getAllExtractedCountries() -> [Country] { [] }
}

public struct ComplyCubeSettings {
    public let clientID: String
    public let clientToken: String
    public let workflowTemplateId: String?
    public let stages: [ComplyCubeMobileSDKStage]
    public let countries: [Country]
    public let lookAndFeel: LookAndFeel?
    public let enableComplete : CompleteScreenInfo?
    public let colorScheme: ComplyCubeColourScheme?
}

public struct CompleteScreenInfo {
    var title: String?
    var message: String?
    var enableCompleteScreen: Bool = false
    
    init() {
        
    }
    
    init(
        enableCompleteScreen: Bool,
        title: String?,
        message: String?
    ) {
        self.enableCompleteScreen = enableCompleteScreen
        self.title = title
        self.message = message
    }
}
 

public struct SettingsDependencies {
    public var stageListParser: StageListParsing?
    public var lookAndFeelParser: LookAndFeelParsing?
    public var colorSchemeParser: ColorSchemeParsing?
    public var countryExtractor: CountryExtracting?

    public init(stageListParser: StageListParsing? = nil,
                lookAndFeelParser: LookAndFeelParsing? = nil,
                colorSchemeParser: ColorSchemeParsing? = nil,
                countryExtractor: CountryExtracting? = nil) {
        self.stageListParser = stageListParser
        self.lookAndFeelParser = lookAndFeelParser
        self.colorSchemeParser = colorSchemeParser
        self.countryExtractor = countryExtractor
    }
}

public extension ComplyCubeSettings {
    static func make(from raw: [String: Any],
                     deps: SettingsDependencies = .init()) throws -> ComplyCubeSettings {

        let v = try ConfigValidator.validate(raw)

        var sdkStages: [ComplyCubeMobileSDKStage] = []
        if v.workflowTemplateId == nil {
            guard let rawStages = raw[ConfigValidator.Key.stages] as? [Any] else {
                throw ConfigurationError.missing(field: ConfigValidator.Key.stages)
            }
            guard let stageParser = deps.stageListParser else {
                throw ConfigurationError.parsersMissing(reason: "Stages provided but no StageListParsing was injected")
            }
            do { sdkStages = try stageParser.parseStages(from: rawStages) }
            catch {
            throw ConfigurationError.parserFailed(stageType: "stages", underlying: String(describing: error))
            }
        }

        var countries: [Country] = []
        if let rawCountries = raw[ConfigValidator.Key.countries] {
            if let extractor = deps.countryExtractor {
                countries = try extractor.extract(from: rawCountries)
            } else if let codes = rawCountries as? [String] {
                countries = codes.map { Country(code: $0) }
            } else {
                throw ConfigurationError.invalidType(field: ConfigValidator.Key.countries,
                                                     expected: "[String]",
                                                     actual: String(describing: type(of: rawCountries)))
            }
        }

        if let extractor = deps.countryExtractor {
            let extracted = extractor.getAllExtractedCountries()
            if !extracted.isEmpty {
                let set = Set(countries.map { $0.code })
                countries.append(contentsOf: extracted.filter { !set.contains($0.code) })
            }
        }

        var lookAndFeel: LookAndFeel? = nil
        if let rawLNF = raw[ConfigValidator.Key.lookAndFeel] as? [String: Any],
           let parser = deps.lookAndFeelParser {
            lookAndFeel = try parser.parseSDK(from: rawLNF)
        }

        var colorScheme: ComplyCubeColourScheme? = nil
        if let rawScheme = raw[ConfigValidator.Key.scheme] as? [String: Any],
           let parser = deps.colorSchemeParser {
            colorScheme = try parser.parseSDK(from: rawScheme)
        }
        
        let enableComplete = containsCompleteAny(raw[ConfigValidator.Key.stages])


        return ComplyCubeSettings(
            clientID: v.clientID,
            clientToken: v.clientToken,
            workflowTemplateId: v.workflowTemplateId,
            stages: sdkStages,
            countries: countries,
            lookAndFeel: lookAndFeel,
            enableComplete: enableComplete,
            colorScheme: colorScheme
        )
    }

    private static func containsCompleteAny(_ any: Any?) -> CompleteScreenInfo {
        guard let any = any else { return CompleteScreenInfo() }

        func norm(_ s: String) -> String {
            s.trimmingCharacters(in: .whitespacesAndNewlines).lowercased()
        }

        // Unwrap Optional<Any> values safely (covers Optional(...) boxed cases)
        func unwrapOptional(_ value: Any) -> Any? {
            let mirror = Mirror(reflecting: value)
            guard mirror.displayStyle == .optional else { return value }
            return mirror.children.first?.value
        }

        guard let value = unwrapOptional(any) else { return CompleteScreenInfo() }

        let needle = "complete"

        // 1) Array container (your real input)
        if let arr = value as? [Any] {
            for item in arr {
                let info = containsCompleteAny(item)
                if info.enableCompleteScreen { return info }
            }
            return CompleteScreenInfo()
        }

        if let arr = value as? NSArray {
            for item in arr {
                let info = containsCompleteAny(item)
                if info.enableCompleteScreen { return info }
            }
            return CompleteScreenInfo()
        }

        // 2) Dictionary case:
        // Treat as "complete" ONLY when name == "complete".
        // If title/message exist in same dict, include them.
        if let dict = value as? [String: Any] {
            if let name = dict["name"] as? String, norm(name) == needle {
                let title = dict["title"] as? String
                let message = dict["message"] as? String
                return CompleteScreenInfo(
                    enableCompleteScreen: true,
                    title: title,
                    message: message
                )
            }

            // Recurse through values to find nested complete payload
            for v in dict.values {
                let info = containsCompleteAny(v)
                if info.enableCompleteScreen { return info }
            }
            return CompleteScreenInfo()
        }

        if let dict = value as? NSDictionary {
            if let name = dict["name"] as? String, norm(name) == needle {
                let title = dict["title"] as? String
                let message = dict["message"] as? String
                return CompleteScreenInfo(
                    enableCompleteScreen: true,
                    title: title,
                    message: message
                )
            }

            for v in dict.allValues {
                let info = containsCompleteAny(v)
                if info.enableCompleteScreen { return info }
            }
            return CompleteScreenInfo()
        }

        // 3) String case:
        // "complete" (or contains "complete") -> enableCompleteScreen = true, no title/message
        if let str = value as? String {
            if norm(str).contains(needle) {
                return CompleteScreenInfo(enableCompleteScreen: true, title: nil, message: nil)
            }
            return CompleteScreenInfo()
        }

        return CompleteScreenInfo()
    }


}
