//
//  VDS-LabelAttributeModel.swift
//  MVMCoreUI
//
//  Created by Matt Bruce on 1/26/23.
//  Copyright © 2023 Verizon Wireless. All rights reserved.
//

import Foundation
import VDS
import Combine

//Meant to be used to convert Any Array of type LabelAttributeModel
extension Array where Element: MVMCoreUI.LabelAttributeModel {
    public func toVDSLabelAttributeModel(delegateObject: MVMCoreUIDelegateObject?, additionalData: [AnyHashable: Any]?) -> [any VDS.LabelAttributeModel] {
        var attributes: [any VDS.LabelAttributeModel] = []
        forEach { atomicLabelAttribute in
            if let attr = atomicLabelAttribute as? (any VDSLabelAttributeConvertable),
                let vds = attr.convertToVDSLabelAttirbute(delegateObject: delegateObject,
                                                          additionalData: additionalData){
                attributes.append(vds)
            }
        }
        return attributes
    }
}

//VDS Convertable Protocol and Extensions
public protocol VDSLabelAttributeConvertable<LabelAttributeType> {
    associatedtype LabelAttributeType: VDS.LabelAttributeModel
    func convertToVDSLabelAttirbute(delegateObject: MVMCoreUIDelegateObject?, additionalData: [AnyHashable: Any]?) -> LabelAttributeType?
}

extension LabelAttributeUnderlineModel: VDSLabelAttributeConvertable {
    
    public func convertToVDSLabelAttirbute(delegateObject: MVMCoreUIDelegateObject?, additionalData: [AnyHashable: Any]?) -> UnderlineLabelAttribute? {
        guard let style = UnderlineLabelAttribute.Style(rawValue: style.rawValue) else { return nil }
        
        var pattern: UnderlineLabelAttribute.Pattern?
        if let attrPattern = pattern {
            pattern = UnderlineLabelAttribute.Pattern(rawValue: attrPattern.rawValue)
        }
        
        return VDS.UnderlineLabelAttribute(location: location,
                                           length: length,
                                           style: style,
                                           color: color?.uiColor,
                                           pattern: pattern)
    }
}

extension LabelAttributeActionModel: VDSLabelAttributeConvertable {
    public func convertToVDSLabelAttirbute(delegateObject: MVMCoreUIDelegateObject?, additionalData: [AnyHashable: Any]?) -> ActionLabelAttribute? {
        var vdsAttribute = VDS.ActionLabelAttribute(location: location, length: length)
        vdsAttribute.subscriber = vdsAttribute.action.sink { [weak self] in
            guard let self else { return }
            MVMCoreUIActionHandler.performActionUnstructured(with: self.action,
                                                             sourceModel: nil,
                                                             additionalData: additionalData,
                                                             delegateObject: delegateObject)
        }
        return vdsAttribute
    }
}

extension LabelAttributeFontModel: VDSLabelAttributeConvertable {
    public func convertToVDSLabelAttirbute(delegateObject: MVMCoreUIDelegateObject?, additionalData: [AnyHashable: Any]?) -> TextStyleLabelAttribute? {
        
        var textStyle: TextStyle?
        if let found = style?.vdsTextStyle() {
            textStyle = found
        } else if let name, let found = TextStyle(rawValue: name) {
            textStyle = found
        }
        
        guard let textStyle else { return nil }
        
        return TextStyleLabelAttribute(location: location,
                                       length: length,
                                       textStyle: textStyle)
    }
}

extension LabelAttributeColorModel: VDSLabelAttributeConvertable {
    public func convertToVDSLabelAttirbute(delegateObject: MVMCoreUIDelegateObject?, additionalData: [AnyHashable: Any]?) -> ColorLabelAttribute? {
        guard let textColor else { return nil }
        return ColorLabelAttribute(location: location,
                                   length: length,
                                   color: textColor.uiColor)
    }
}

extension LabelAttributeStrikeThroughModel: VDSLabelAttributeConvertable {
    public func convertToVDSLabelAttirbute(delegateObject: MVMCoreUIDelegateObject?, additionalData: [AnyHashable: Any]?) -> StrikeThroughLabelAttribute? {
        return StrikeThroughLabelAttribute(location: location,
                                           length: length)
    }
}

extension LabelAttributeImageModel: VDSLabelAttributeConvertable {
    public func convertToVDSLabelAttirbute(delegateObject: MVMCoreUIDelegateObject?, additionalData: [AnyHashable: Any]?) -> AtomicImageLabelAttribute? {
        var frame: CGRect?
        if let size {
            frame = CGRect(x: 0, y: 0, width: size, height: size)
        }
        return AtomicImageLabelAttribute(location: location,
                                         length: length,
                                         imageName: name,
                                         imageURL: URL,
                                         frame: frame,
                                         tintColor: tintColor?.uiColor)
    }
}

//Custom Atomic Additions for the ImageAttachment
public class AtomicImageLabelAttribute: AttachmentLabelAttributeModel {
    //--------------------------------------------------
    // MARK: - Enums
    //--------------------------------------------------
    public enum Error: Swift.Error {
        case bundleNotFound
        case imageNotFound(String)
        case imageNotSet
    }
    
    //--------------------------------------------------
    // MARK: - Public Properties
    //--------------------------------------------------
    public var id = UUID()
    public var location: Int
    public var length: Int = 1
    public var imageName: String?
    public var imageURL: String?
    public var image: UIImage?
    public var frame: CGRect?
    public var tintColor: UIColor?
    
    //--------------------------------------------------
    // MARK: - Public Properties
    //--------------------------------------------------
    public init(location: Int, length: Int = 1, imageName: String? = nil, imageURL: String? = nil,
                image: UIImage? = nil, frame: CGRect? = nil, tintColor: UIColor?) {
        self.location = location
        self.length = length
        self.imageName = imageName
        self.imageURL = imageURL
        self.image = image
        self.frame = frame
        self.tintColor = tintColor
        fetchImage()
    }
    
    //--------------------------------------------------
    // MARK: - Equatable
    //--------------------------------------------------
    public static func == (lhs: AtomicImageLabelAttribute, rhs: AtomicImageLabelAttribute) -> Bool {
        lhs.isEqual(rhs)
    }
    
    public func isEqual(_ equatable: AtomicImageLabelAttribute) -> Bool {
        return id == equatable.id && range == equatable.range && imageName == equatable.imageName && imageURL == equatable.imageURL
    }

    //--------------------------------------------------
    // MARK: - Private Functions
    //--------------------------------------------------
    private func fetchImage() {
        if image != nil { return }
        
        //get a local asset
        if let imageName {
            if let coreImage = MVMCoreCache.shared()?.getImageFromRegisteredBundles(imageName) {
                image = coreImage
            }
            
            if let vdsImage =  BundleManager.shared.image(for: imageName) {
                image = vdsImage
            }
        }//get from url
        else if let imageURL {
            DispatchQueue.global(qos: .default).async {
                MVMCoreCache.shared()?.getImage(imageURL, useWidth: false, widthForS7: 0, useHeight: false, heightForS7: 0, localFallbackImageName: nil) { [weak self] image, data, _ in
                    DispatchQueue.main.sync {
                        self?.image = image
                    }
                }
            }
        }
    }
    
    //--------------------------------------------------
    // MARK: - Public Functions
    //--------------------------------------------------
    public func getAttachment() throws -> NSTextAttachment {
        if let image {
            let attachment = NSTextAttachment()
            attachment.image = tintColor != nil ? image.withTintColor(tintColor!) : image
            attachment.bounds = frame ?? .init(x: 0, y: 0, width: image.size.width, height: image.size.height)
            return attachment
        } else {
            throw Error.imageNotSet
        }
    }
}

