//
//  PillButton.swift
//  MVMCoreUI
//
//  Created by Scott Pfeil on 1/28/20.
//  Copyright © 2020 Verizon Wireless. All rights reserved.
//

import UIKit
import VDSColorTokens

open class PillButton: Button, MVMCoreUIViewConstrainingProtocol {
    //--------------------------------------------------
    // MARK: - Properties
    //--------------------------------------------------
    
    /// Used to size the button.
    var size = MVMCoreUIUtility.getWidth()
    
    var buttonModel: ButtonModel? {
        get { model as? ButtonModel }
    }
    
    /// Need to re-style on set.
    open override var isEnabled: Bool {
        didSet { style(with: buttonModel) }
    }
    
    open var buttonSize: Styler.Button.Size = .standard {
        didSet { buttonModel?.size = buttonSize }
    }
    
    //--------------------------------------------------
    // MARK: - Constraints
    //--------------------------------------------------
    
    public var widthConstraint: NSLayoutConstraint?
    public var minimumWidthConstraint: NSLayoutConstraint?
    
    //--------------------------------------------------
    // MARK: - Initializers
    //--------------------------------------------------
    
    @objc public convenience init(asPrimaryButton isPrimary: Bool, makeTiny istiny: Bool) {
        let model = ButtonModel(with: "", action: ActionNoopModel())
        model.style = isPrimary ? .primary : .secondary
        model.size = istiny ? .tiny : .standard
        self.init(model: model, nil, nil)
    }
    
    //--------------------------------------------------
    // MARK: - Computed Properties
    //--------------------------------------------------
    
    public var enabledTitleColor: UIColor? {
        get { titleColor(for: .normal) }
        set { setTitleColor(newValue, for: .normal) }
    }
    
    public var disabledTitleColor: UIColor? {
        get { titleColor(for: .disabled) }
        set { setTitleColor(newValue, for: .disabled) }
    }
    
    public var borderColor: UIColor? {
        get {
            guard let currentColor = layer.borderColor else { return nil }
            return UIColor(cgColor: currentColor)
        }
        set { layer.borderColor = newValue?.cgColor }
    }
    
    //--------------------------------------------------
    // MARK: - Methods
    //--------------------------------------------------
    
    /// The primary styling for a button. Should be used for main buttons
    public func stylePrimary() {
        let buttonModel = ButtonModel(primaryButtonWith: "", action: ActionNoopModel())
        style(with: buttonModel)
    }
    
    /// The secondary styling for a button. Should be used for secondary buttons
    public func styleSecondary() {
        let buttonModel = ButtonModel(secondaryButtonWith: "", action: ActionNoopModel())
        style(with: buttonModel)
    }
    
    /// Styles the button based on the model style
    private func style(with model: ButtonModel?) {
        
        layer.borderWidth = model?.style == .secondary ? 1 : 0
        
        if let titleColor = model?.enabledColors.text {
            enabledTitleColor = titleColor
        }
        
        if let disabledTitleColor = model?.disabledColors.text {
            self.disabledTitleColor = disabledTitleColor
        }
        
        #if DEBUG
        // Useful to detect with isHittable when performing UI testing.
        isAccessibilityElement = isEnabled
        #endif
        
        if isEnabled {
            if let fillColor = model?.enabledColors.fill {
                backgroundColor = fillColor
            }
            
            if let borderColor = model?.enabledColors.border {
                self.borderColor = borderColor
            }
        } else {
            if let fillColor = model?.disabledColors.fill {
                backgroundColor = fillColor
            }
            
            if let borderColor = model?.disabledColors.border {
                self.borderColor = borderColor
            }
        }
    }
    
    private func getInnerPadding() -> CGFloat {
        buttonSize.getHeight() / 2.0
    }
        
    private func getContentEdgeInsets() -> UIEdgeInsets {
        var verticalPadding = 0.0
        var horizontalPadding = 0.0
        switch buttonSize {
        case .standard:
            verticalPadding = Padding.Three
            horizontalPadding = Padding.Five
            break
        case .small:
            verticalPadding = Padding.Two
            horizontalPadding = Padding.Four
            break
        case .tiny:
            verticalPadding = Padding.One
            horizontalPadding = Padding.Two
            break
        }
        return UIEdgeInsets(top: verticalPadding, left: horizontalPadding, bottom: verticalPadding, right: horizontalPadding)
    }
    
    //--------------------------------------------------
    // MARK: - MVMCoreViewProtocol
    //--------------------------------------------------
    
    open override func set(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?, _ additionalData: [AnyHashable: Any]?) {
        // The button will get styled in the enable check in super.
        super.set(with: model, delegateObject, additionalData)
        
        guard let model = model as? ButtonModel else { return }
        
        setTitle(model.title, for: .normal)
        if let accessibilityText = model.accessibilityText {
            accessibilityLabel = accessibilityText
        }
        
        if let size = model.size {
            buttonSize = size
        }
        
        model.updateUI = { [weak self] in
            MVMCoreDispatchUtility.performBlock(onMainThread: {
                self?.enableField(model.enabled)
            })
        }
        
        FormValidator.setupValidation(for: model, delegate: delegateObject?.formHolderDelegate)
    }
    
    open override class func estimatedHeight(with model: MoleculeModelProtocol, _ delegateObject:  MVMCoreUIDelegateObject?) -> CGFloat? {
        return (model as? ButtonModel)?.size?.getHeight()
    }
    
    open override func updateView(_ size: CGFloat) {
        super.updateView(size)
        self.size = size
        
        switch buttonSize {
        case .tiny:
            titleLabel?.font = Styler.Font.BoldMicro.getFont(false)
        case .small:
            titleLabel?.font = Styler.Font.BoldBodySmall.getFont(false)
        case .standard:
            titleLabel?.font = Styler.Font.BoldBodyLarge.getFont(false)
        }
        
        layer.cornerRadius = getInnerPadding()
        contentEdgeInsets = getContentEdgeInsets()
        
        if let contraint = buttonModel?.width {
            
            if widthConstraint == nil {
                widthConstraint = widthAnchor.constraint(equalToConstant: contraint)
            } else if widthConstraint?.constant != contraint {
                widthConstraint?.constant = contraint
            }
            widthConstraint?.isActive = true
            minimumWidthConstraint?.isActive = false
        } else {
            
            if minimumWidthConstraint == nil {
                minimumWidthConstraint = widthAnchor.constraint(greaterThanOrEqualToConstant: buttonSize.minimumWidth())
            } else {
                minimumWidthConstraint?.constant = buttonSize.minimumWidth()
            }
            minimumWidthConstraint?.isActive = true
            widthConstraint?.isActive = false
        }
    }
    
    open override func setupView() {
        super.setupView()
        
        titleLabel?.numberOfLines = 1
        titleLabel?.lineBreakMode = .byTruncatingTail
        titleLabel?.textAlignment = .center
        contentHorizontalAlignment = .center
        stylePrimary()
    }
    
    //--------------------------------------------------
    // MARK: - MVMCoreUIViewConstrainingProtocol
    //--------------------------------------------------
    
    open func horizontalAlignment() -> UIStackView.Alignment { .center }
    
    public func enableField(_ enable: Bool) {
        isEnabled = enable
    }
}
