//
//  RichTextView.swift
//  RichTextView
//
//  Created by Ahmed Elkady on 2018-11-08.
//  Copyright © 2018 Top Hat. All rights reserved.
//

import SnapKit
import WebKit

public class RichTextView: UIView {

    // MARK: - Properties

    public private(set) var input: String
    private(set) var richTextParser: RichTextParser
    private(set) var textColor: UIColor
    private(set) var isSelectable: Bool
    private(set) var isEditable: Bool
    public weak var textViewDelegate: RichTextViewDelegate?

    private(set) var errors: [ParsingError]?

    // MARK: - Constants

    private enum VideoProperties {
        static let defaultAspectRatio = 9.0/16.0
    }

    // MARK: - Init

    @objc public init(input: String = "",
                latexParser: LatexParserProtocol = LatexParser(),
                font: UIFont = UIFont.systemFont(ofSize: UIFont.systemFontSize),
                textColor: UIColor = UIColor.black,
                isSelectable: Bool = true,
                isEditable: Bool = false,
                latexTextBaselineOffset: CGFloat = 0,
                interactiveTextColor: UIColor = UIColor.blue,
                textViewDelegate: RichTextViewDelegate? = nil,
                customAdditionalAttributes: [String: [NSAttributedString.Key: Any]]? = nil,
                frame: CGRect,
                completion: (([Error]?) -> Void)? = nil) {
        self.input = input
        self.isSelectable = isSelectable
        self.isEditable = isEditable
        self.richTextParser = RichTextParser(
            latexParser: latexParser,
            font: font,
            textColor: textColor,
            latexTextBaselineOffset: latexTextBaselineOffset,
            interactiveTextColor: interactiveTextColor,
            customAdditionalAttributes: customAdditionalAttributes
        )
        self.textColor = textColor
        self.textViewDelegate = textViewDelegate
        super.init(frame: frame)
        self.setupSubviews()
        completion?(self.errors)
    }

    required init?(coder aDecoder: NSCoder) {
        self.input = ""
        self.richTextParser = RichTextParser()
        self.textColor = UIColor.black
        self.isSelectable = true
        self.isEditable = false
        super.init(coder: aDecoder)
        self.setupSubviews()
    }

    // MARK: - Helpers

    public func update(input: String? = nil,
                       latexParser: LatexParserProtocol? = nil,
                       font: UIFont? = nil,
                       textColor: UIColor? = nil,
                       latexTextBaselineOffset: CGFloat? = nil,
                       interactiveTextColor: UIColor? = nil,
                       customAdditionalAttributes: [String: [NSAttributedString.Key: Any]]? = nil,
                       completion: (([ParsingError]?) -> Void)? = nil) {
        self.input = input ?? self.input
        self.richTextParser = RichTextParser(
            latexParser: latexParser ?? self.richTextParser.latexParser,
            font: font ?? self.richTextParser.font,
            textColor: textColor ?? self.textColor,
            latexTextBaselineOffset: latexTextBaselineOffset ?? self.richTextParser.latexTextBaselineOffset,
            interactiveTextColor: interactiveTextColor ?? self.richTextParser.interactiveTextColor,
            customAdditionalAttributes: customAdditionalAttributes ?? self.richTextParser.customAdditionalAttributes
        )
        self.textColor = textColor ?? self.textColor
        self.subviews.forEach { $0.removeFromSuperview() }
        self.setupSubviews()
        completion?(self.errors)
    }

    private func setupSubviews() {
        let subviews = self.generateViews()
        for (index, subview) in subviews.enumerated() {
            self.addSubview(subview)
            subview.snp.makeConstraints { make in
                if index == 0 {
                    make.top.equalTo(self)
                } else {
                    make.top.equalTo(subviews[index - 1].snp.bottom)
                }
                make.width.equalTo(self)
                make.centerX.equalTo(self)
                if subview is WKWebView {
                    make.height.equalTo(self.snp.width).multipliedBy(VideoProperties.defaultAspectRatio)
                }
                if index == subviews.count - 1 {
                    make.bottom.equalTo(self)
                }
            }
            subview.backgroundColor = UIColor.clear
        }
        self.enableAccessibility()
    }

    func generateViews() -> [UIView] {
        return self.richTextParser.getRichDataTypes(from: self.input).compactMap { (richDataType: RichDataType) -> UIView? in
            switch richDataType {
            case .video(let tag, let error):
                self.appendError(error)
                let webView = WKWebViewGenerator.getWebView(from: tag)
                if webView == nil {
                    self.appendError(ParsingError.webViewGeneration(link: tag))
                }
                return webView
            case .text(let richText, let font, let errors):
                self.appendErrors(errors)
                return UITextViewGenerator.getTextView(
                    from: richText,
                    font: font,
                    textColor: self.textColor,
                    isSelectable: self.isSelectable,
                    isEditable: self.isEditable,
                    textViewDelegate: self.textViewDelegate
                )
            }
        }
    }

    private func appendErrors(_ errors: [ParsingError]?) {
        if let errors = errors {
            if self.errors == nil {
                self.errors = [ParsingError]()
            }
            self.errors?.append(contentsOf: errors)
        }
    }

    private func appendError(_ error: ParsingError?) {
        if let error = error {
            if self.errors == nil {
                self.errors = [ParsingError]()
            }
            self.errors?.append(error)
        }
    }

    private func enableAccessibility() {
        self.isAccessibilityElement = true
        self.accessibilityValue = nil
        for view in self.subviews {
            if let accessibilityValue = view.accessibilityValue, !accessibilityValue.isEmpty {
                if self.accessibilityValue == nil {
                    self.accessibilityValue = accessibilityValue
                } else {
                    self.accessibilityValue?.append(accessibilityValue)
                }
            }
        }
    }
}
