import Foundation
import ComplyCubeMobileSDK

/// Expands country-based metadata templates (e.g., TIN flows) into SDK fields.
public final class MetadataTemplateProcessor {

    public init() {}

    /// Appends metadata fields generated from templates for each country (except US/CA).
    ///
    /// - Parameters:
    ///   - filteredCountries: Countries already selected/derived upstream.
    ///   - countrySelectKey: The key used by the country selector question (for constraints).
    ///   - metadataTemplates: Array of template maps with keys: templateKey, question, description, componentType, options, format.
    ///   - customerInfoFields: Output array of SDK fields to be appended to.
    public func processCountryTemplates(
        filteredCountries: [Country],
        countrySelectKey: String,
        metadataTemplates: [[String: Any]],
        customerInfoFields: inout [CustomerInformationField]
    ) {
        filteredCountries
            .filter { $0.code != "US" && $0.code != "CA" } // parity with Android
            .forEach { country in
                let key = country.code
                // If caller didn't pass a custom selector key, use a readable default like Android.
                let effectiveKey = countrySelectKey.isEmpty ? "Tax residences" : countrySelectKey

                // 1) ask if TIN exists for this country
                customerInfoFields.append(
                    injectTemplate(
                        baseKey: key,
                        constraintExpr: "metadata.\(effectiveKey) contains \(key)",
                        templates: metadataTemplates,
                        templateKey: "TIN_HAS"
                    )
                )

                // 2) if yes → ask TIN
                customerInfoFields.append(
                    injectTemplate(
                        baseKey: key,
                        constraintExpr: "metadata.\(key)_TIN_HAS contains yes",
                        templates: metadataTemplates,
                        templateKey: "TIN"
                    )
                )

                // 3) if no → ask reason
                customerInfoFields.append(
                    injectTemplate(
                        baseKey: key,
                        constraintExpr: "metadata.\(key)_TIN_HAS contains no",
                        templates: metadataTemplates,
                        templateKey: "TIN_REASON"
                    )
                )

                // 4) if reason is "other" → ask to specify
                customerInfoFields.append(
                    injectTemplate(
                        baseKey: key,
                        constraintExpr: "metadata.\(key)_TIN_REASON contains other",
                        templates: metadataTemplates,
                        templateKey: "TIN_REASON_OTHER"
                    )
                )
            }
    }

    // MARK: - Internals

    private func template(with key: String, in templates: [[String: Any]]) -> [String: Any]? {
        templates.first { ($0["templateKey"] as? String) == key }
    }

    @inline(__always)
    private func injectTemplate(
        baseKey: String,
        constraintExpr: String,
        templates: [[String: Any]],
        templateKey: String
    ) -> CustomerInformationField {
        guard let tpl = template(with: templateKey, in: templates) else {
            // Keep behavior simple for now; you can throw instead if you prefer.
            fatalError("Missing template: \(templateKey)")
        }

        // Texts (inject {country})
        let q = (tpl["question"] as? String)?.replacingOccurrences(of: "{country}", with: baseKey) ?? ""
        let desc = (tpl["description"] as? String)?.replacingOccurrences(of: "{country}", with: baseKey)

        // Component type (string → SDK enum)
        let compType = parseComponentType(tpl["componentType"] as? String)

        // Options (array → [value: label])
        let opts: [CustomerInformationField.Value : CustomerInformationField.Label]? = {
            guard let arr = tpl["options"] as? [[String: Any]] else { return nil }
            var dict: [String: String] = [:]
            for item in arr {
                let label = item["label"] as? String ?? ""
                let value = item["value"] as? String ?? ""
                dict[value] = label
            }
            return dict.isEmpty ? nil : dict
        }()

        // Optional format
        var formatModel: Format? = nil
        if let fmt = tpl["format"] as? [String: Any] {
            formatModel = decodeCodable(Format.self, from: fmt)
        }

        // Build the metadata field (note: constraint is a String per SDK)
        return .metaData(
            key: "\(baseKey)_\(templateKey)",
            question: q,
            componentType: compType,
            format: formatModel,
            options: opts,
            required: true,
            description: desc,
            placeholder: nil,
            constraint: constraintExpr,
            dataList: nil
        )
    }

    private func parseComponentType(_ raw: String?) -> ComponentType {
        switch (raw ?? "").uppercased() {
        case "SHORT_ANSWER":         return .shortAnswer
        case "PARAGRAPH":            return .paragraph
        case "SINGLE_CHOICE":        return .singleSelection
        case "MULTI_CHOICE":         return .multipleChoice
        case "MULTI_SELECT_COUNTRY": return .countries
        case "DATE":                 return .date
        case "COUNTRY":              return .country
        case "NUMBER":               return .phoneNumber
        case "PHONE":                return .phoneNumber
        default:                     return .shortAnswer
        }
    }
    
    private func decodeCodable<T: Decodable>(_ type: T.Type, from dict: [String: Any]) -> T? {
        guard JSONSerialization.isValidJSONObject(dict),
              let data = try? JSONSerialization.data(withJSONObject: dict, options: []) else { return nil }
        return try? JSONDecoder().decode(T.self, from: data)
    }
}
