//
//  ButtonGroup.swift
//  MVMCoreUI
//
//  Created by Matt Bruce on 1/11/24.
//  Copyright © 2024 Verizon Wireless. All rights reserved.
//

import Foundation
import UIKit
import VDS
import Combine

open class ButtonGroup: VDS.ButtonGroup, VDSMoleculeViewProtocol {
    
    //--------------------------------------------------
    // MARK: - Properties
    //--------------------------------------------------
    public var model: MoleculeModelProtocol?
    open var viewModel: ButtonGroupModel!
    open var delegateObject: MVMCoreUIDelegateObject?
    open var additionalData: [AnyHashable : Any]?
    open var previousModel: ButtonGroupModel?
    
    /// For notifying the delegate of layout updates.
    private var contentSizeObservation: Cancellable?
    private var previousSize: CGSize?

    //--------------------------------------------------
    // MARK: - MoleculeViewProtocol
    //--------------------------------------------------
    
    public static func estimatedHeight(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?) -> CGFloat? {
        guard let model = model as? ButtonGroupModel,
              let buttonModel = model.buttons.first
        else { return 0 }
        
        return PillButton.estimatedHeight(with: buttonModel, delegateObject)
    }
    
    public func viewModelDidUpdate() {                
        surface = viewModel.surface
        isEnabled = viewModel.enabled
        alignment = viewModel.alignment
        rowQuantityPhone = viewModel.rowQuantityPhone
        rowQuantityTablet = viewModel.rowQuantityTablet
        
        if let childWidthValue = viewModel.childWidthValue {
            childWidth = .value(childWidthValue)
        } else if let childWidthPercentage = viewModel.childWidthPercentage {
            childWidth = .percentage(childWidthPercentage)
        } else {
            childWidth = nil
        }
        
        // create / set the moleculeViews
        if (previousModel == nil) || Self.nameForReuse(with: previousModel!, delegateObject) != Self.nameForReuse(with: viewModel, delegateObject) {
            previousModel = viewModel
            var buttonBases = [ButtonBase]()
            viewModel.buttons.forEach { buttonModel in                
                do {
                    let buttonBaseType = try ModelRegistry.getHandler(buttonModel) as! MoleculeViewProtocol.Type
                    let button = buttonBaseType.init(model: buttonModel, delegateObject, additionalData) as! VDS.ButtonBase
                    buttonBases.append(button)
                } catch {
                    if let errorObject = MVMCoreErrorObject.createErrorObject(for: error, location: #function) {
                        MVMCoreLoggingHandler.shared()?.addError(toLog: errorObject)
                    }
                }
            }
            buttons = buttonBases
            
        } else {
            // only set the molecule views
            for (index, buttonBase) in buttons.enumerated() {
                if let button = buttonBase as? MoleculeViewProtocol, index < viewModel.buttons.count {
                    button.set(with: viewModel.buttons[index], delegateObject, additionalData)
                }
            }
            // force redraw
            setNeedsUpdate()
        }
        
        // Notify the delegate of a size change.
        previousSize = bounds.size
        contentSizeObservation = contentSizePublisher.sink { [weak self] size in
            guard let self = self,
                  !MVMCoreGetterUtility.fequal(a: Float(size.height), b: Float(self.previousSize?.height ?? Self.estimatedHeight(with: self.viewModel, self.delegateObject) ?? VDS.Button.Size.large.height)) else { return }
            self.previousSize = size
            self.delegateObject?.moleculeDelegate?.moleculeLayoutUpdated(self)
        }
    }
    
    //--------------------------------------------------
    // MARK: - MVMCoreUIViewConstrainingProtocol
    //--------------------------------------------------
    
    open func horizontalAlignment() -> UIStackView.Alignment {
        return .center
    }
    
    //--------------------------------------------------
    // MARK: - MVMCoreViewProtocol
    //--------------------------------------------------
    public func updateView(_ size: CGFloat) {
        setNeedsUpdate()
    }
    
    public static func nameForReuse(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?) -> String? {
        // This will aggregate names of molecules to make an id.
        guard let model = model as? ButtonGroupModel else {
            return "buttonGroup<>"
        }
        var name = "buttonGroup<"
        for case let item in model.buttons {
            if let moleculeClass = ModelRegistry.getMoleculeClass(item),
                let nameForReuse = moleculeClass.nameForReuse(with: item, delegateObject) {
                name.append(nameForReuse + ",")
            } else {
                name.append(item.moleculeName + ",")
            }
        }
        name.append(">")
        return name
    }
}
