//
// Copyright 2024 New Vector Ltd.
// Copyright 2020 The Matrix.org Foundation C.I.C
//
// SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
// Please see LICENSE files in the repository root for full details.
//

import DTCoreText

public extension DTHTMLElement {
    typealias ImageHandler = (_ sourceURL: String, _ width: CGFloat, _ height: CGFloat) -> URL?
    
    /// Sanitize the element using the given parameters.
    /// - Parameters:
    ///   - allowedHTMLTags: An array of tags that are allowed. All other tags will be removed.
    ///   - font: The default font to use when resetting the content of any unsupported tags.
    ///   - imageHandler: An optional image handler to be run on `img` tags (if allowed) to update the `src` attribute.
    @objc func sanitize(with allowedHTMLTags: [String], bodyFont font: UIFont, imageHandler: ImageHandler?) {
        if let name = name, !allowedHTMLTags.contains(name) {
            
            // This is an unsupported tag.
            // Remove any attachments to fix rendering.
            textAttachment = nil
            
            // Handle special case for span with data-mx-external-payment-details
            // This could be based on Storefront.current.countryCode to show the link
            // content in unrestricted countries. e.g. currently USA
            if name == "span",
               let attributes = attributes as? [String: String],
               attributes["data-msc4286-external-payment-details"] != nil {
                parent.removeChildNode(self)
                return
            }
            
            // If the element has plain text content show that,
            // otherwise prevent the tag from displaying.
            if let stringContent = attributedString()?.string,
               !stringContent.isEmpty,
               let element = DTTextHTMLElement(name: nil, attributes: nil) {
                element.setText(stringContent)
                removeAllChildNodes()
                addChildNode(element)
                
                if let parent = parent() {
                    element.inheritAttributes(from: parent)
                } else {
                    fontDescriptor = DTCoreTextFontDescriptor()
                    fontDescriptor.fontFamily = font.familyName
                    fontDescriptor.fontName = font.fontName
                    fontDescriptor.pointSize = font.pointSize
                    paragraphStyle = DTCoreTextParagraphStyle.default()
                    
                    element.inheritAttributes(from: self)
                }
                element.interpretAttributes()
                
            } else if let parent = parent() {
                parent.removeChildNode(self)
            } else {
                didOutput = true
            }
            
        } else {
            // Process images with the handler when self is an image tag.
            if name == "img", let imageHandler = imageHandler {
                process(with: imageHandler)
            }
            
            // This element is a supported tag, but it may contain children that aren't,
            // so santize all child nodes to ensure correct tags.
            if let childNodes = childNodes as? [DTHTMLElement] {
                childNodes.forEach { $0.sanitize(with: allowedHTMLTags, bodyFont: font, imageHandler: imageHandler) }
            }
        }
    }
    
    /// Process the element with the supplied image handler.
    private func process(with imageHandler: ImageHandler) {
        // Get the values required to pass to the image handler
        guard let sourceURL = attributes["src"] as? String else { return }
        
        var width: CGFloat = -1
        if let widthString = attributes["width"] as? String,
           let widthDouble = Double(widthString) {
            width = CGFloat(widthDouble)
        }
        
        var height: CGFloat = -1
        if let heightString = attributes["height"] as? String,
           let heightDouble = Double(heightString) {
            height = CGFloat(heightDouble)
        }
        
        // If the handler returns an updated URL, update the text attachment.
        guard let localSourceURL = imageHandler(sourceURL, width, height) else { return }
        textAttachment.contentURL = localSourceURL
    }
}
