//
//  FormBaseRulesProtocol.swift
//  MVMCoreUI
//
//  Created by Suresh, Kamlesh on 1/31/20.
//  Copyright © 2020 Verizon Wireless. All rights reserved.
//

import Foundation


public enum RulesCodingKey: String, CodingKey {
    case ruleType
}

public protocol RulesProtocol: ModelProtocol {
    // The type of rule
    var type: String { get }
    
    //id for the rule
    var ruleId: String? { get }
    
    //error message to show for the rule
    var errorMessage: [String: String]? { get }

    // The fields that this rule applies to.
    var fields: [String] { get set }

    // Returns if a given field is valid according to the rule
    func isValid(_ formField: FormFieldProtocol) -> Bool

    // Validates the rule and returns the result.
    func validate(_ fieldMolecules: [String: FormFieldProtocol],_ previousFieldValidity: [String: Bool]) -> (valid: Bool, fieldValidity: [String: Bool])
}

public extension RulesProtocol {

    var type: String { Self.identifier }

    static var categoryCodingKey: String { "type" }

    static var categoryName: String { "\(RulesProtocol.self)" }

    // Individual rule can override the function to validate based on the rule type.
    func validate(_ fieldMolecules: [String: FormFieldProtocol],_ previousFieldValidity: [String: Bool]) -> (valid: Bool, fieldValidity: [String: Bool]) {
        var valid = true
        var previousValidity: [String: Bool] = [:]
        for formKey in fields {
            guard let formField = fieldMolecules[formKey] else { continue }

            var fieldValidity = isValid(formField)
            // If past rule is invalid for a field, the current rule should not flip the validity of a field
            if let validity = previousFieldValidity[formKey], !validity, fieldValidity {
                fieldValidity = false
            }
            valid = valid && fieldValidity
            previousValidity[formKey] = fieldValidity
        }
        return (valid: valid, fieldValidity: previousValidity)
    }
}

public protocol RulesContainerProtocol{
    var rules: [RulesProtocol] { get }
}

public extension RulesContainerProtocol {
    /// This validation for Rules for the Validation or for Effects.
    /// - Parameters:
    ///   - fields: Fields for the group
    ///   - setValidity: Since this function is for validation, this bool determines if you should set the FormFields.setValidity for a rule.
    ///       this method can be called for Form Validation and Effect Validation (this doesn't affect the submital of the form)
    /// - Returns: Tuple(valid, fieldValidity)
    ///     - valid: bool for all rules
    ///     - fieldValidity: accumulation of all fieldKey: valid
    func validate(_ fields:  [String: FormFieldProtocol]) -> (valid: Bool, fieldValidity: [String:Bool] ) {
        // Validate each rule.
        var valid = true
        var previousValidity: [String: Bool] = [:]
        for rule in self.rules {
            //validate the rule against the fields
            let tuple = rule.validate(fields, previousValidity)

            //merge the fieldValidity
            previousValidity = previousValidity.merging(tuple.fieldValidity) { (_, new) in new }
            valid = valid && tuple.valid
        }
        return (valid: valid, fieldValidity: previousValidity)
    }
}
