//
//  ProfileFieldValueView.swift
//  Tusker
//
//  Created by Shadowfacts on 5/6/23.
//  Copyright © 2023 Shadowfacts. All rights reserved.
//

import UIKit
import Pachyderm
import SwiftUI
import SafariServices

class ProfileFieldValueView: UIView {
    weak var navigationDelegate: TuskerNavigationDelegate?
    
    private static let converter: HTMLConverter = {
        var converter = HTMLConverter()
        converter.font = .preferredFont(forTextStyle: .body)
        converter.monospaceFont = UIFontMetrics.default.scaledFont(for: .monospacedSystemFont(ofSize: 17, weight: .regular))
        return converter
    }()
    
    private let account: AccountMO
    private let field: Account.Field
    private var link: (String, URL)?
    
    private let label = EmojiLabel()
    private var iconView: UIView?
    
    private var currentTargetedPreview: UITargetedPreview?
    
    init(field: Account.Field, account: AccountMO) {
        self.account = account
        self.field = field
        
        super.init(frame: .zero)
        
        let converted = NSMutableAttributedString(attributedString: ProfileFieldValueView.converter.convert(field.value))
        
        var range = NSRange(location: 0, length: 0)
        if converted.length != 0,
           let url = converted.attribute(.link, at: 0, longestEffectiveRange: &range, in: converted.fullRange) as? URL {
            link = (converted.attributedSubstring(from: range).string, url)
            label.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(linkTapped)))
            label.addInteraction(UIContextMenuInteraction(delegate: self))
            label.isUserInteractionEnabled = true
            
            converted.enumerateAttribute(.link, in: converted.fullRange) { value, range, stop in
                guard value != nil else { return }
                converted.addAttribute(.foregroundColor, value: UIColor.tintColor, range: range)
                // the .link attribute in a UILabel always makes the color blue >.>
                converted.removeAttribute(.link, range: range)
            }
        }
        
        label.numberOfLines = 0
        label.font = .preferredFont(forTextStyle: .body)
        label.adjustsFontForContentSizeCategory = true
        label.attributedText = converted
        label.setEmojis(account.emojis, identifier: account.id)
        label.setContentCompressionResistancePriority(.required, for: .vertical)
        label.translatesAutoresizingMaskIntoConstraints = false
        addSubview(label)
        
        let labelTrailingConstraint: NSLayoutConstraint
        
        if field.verifiedAt != nil {
            var config = UIButton.Configuration.plain()
            config.preferredSymbolConfigurationForImage = UIImage.SymbolConfiguration(scale: .medium)
            config.image = UIImage(systemName: "checkmark")
            config.baseForegroundColor = .systemGreen
            let icon = UIButton(configuration: config)
            self.iconView = icon
            icon.translatesAutoresizingMaskIntoConstraints = false
            icon.setContentHuggingPriority(.defaultHigh, for: .horizontal)
            icon.addTarget(self, action: #selector(verifiedIconTapped), for: .touchUpInside)
            icon.isPointerInteractionEnabled = true
            icon.accessibilityLabel = "Verified link"
            addSubview(icon)
            labelTrailingConstraint = label.trailingAnchor.constraint(equalTo: icon.leadingAnchor)
            NSLayoutConstraint.activate([
                icon.centerYAnchor.constraint(equalTo: label.centerYAnchor),
                icon.trailingAnchor.constraint(lessThanOrEqualTo: trailingAnchor),
            ])
        } else {
            labelTrailingConstraint = label.trailingAnchor.constraint(equalTo: trailingAnchor)
        }
        
        NSLayoutConstraint.activate([
            label.leadingAnchor.constraint(equalTo: leadingAnchor),
            labelTrailingConstraint,
            label.topAnchor.constraint(equalTo: topAnchor),
            label.bottomAnchor.constraint(equalTo: bottomAnchor),
        ])
    }
    
    required init?(coder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
    
    override func sizeThatFits(_ size: CGSize) -> CGSize {
        var size = label.sizeThatFits(size)
        if let iconView {
            size.width += iconView.sizeThatFits(UIView.layoutFittingCompressedSize).width
        }
        return size
    }
    
    func setTextAlignment(_ alignment: NSTextAlignment) {
        label.textAlignment = alignment
    }
    
    func getHashtagOrURL() -> (Hashtag?, URL)? {
        guard let (text, url) = link else {
            return nil
        }
        if text.starts(with: "#") {
            return (Hashtag(name: String(text.dropFirst()), url: url), url)
        } else {
            return (nil, url)
        }
    }
    
    @objc private func linkTapped() {
        guard let (hashtag, url) = getHashtagOrURL() else {
            return
        }
        if let hashtag {
            navigationDelegate?.selected(tag: hashtag)
        } else {
            navigationDelegate?.selected(url: url)
        }
    }
    
    @objc private func verifiedIconTapped() {
        guard let navigationDelegate else {
            return
        }
        let view = ProfileFieldVerificationView(
            acct: account.acct,
            verifiedAt: field.verifiedAt!,
            linkText: label.text ?? "",
            navigationDelegate: navigationDelegate
        )
        let host = UIHostingController(rootView: view)
        let toPresent: UIViewController
        if traitCollection.horizontalSizeClass == .compact || traitCollection.verticalSizeClass == .compact {
            toPresent = UINavigationController(rootViewController: host)
            toPresent.modalPresentationStyle = .pageSheet
            let sheetPresentationController = toPresent.sheetPresentationController!
            sheetPresentationController.detents = [
                .medium()
            ]
        } else {
            host.modalPresentationStyle = .popover
            let popoverPresentationController = host.popoverPresentationController!
            popoverPresentationController.sourceView = iconView
            host.preferredContentSize = host.sizeThatFits(in: CGSize(width: 400, height: CGFloat.infinity))
            toPresent = host
        }
        navigationDelegate.present(toPresent, animated: true)
    }
}

extension ProfileFieldValueView: UIContextMenuInteractionDelegate, MenuActionProvider {
    var toastableViewController: ToastableViewController? {
        navigationDelegate
    }
    
    func contextMenuInteraction(_ interaction: UIContextMenuInteraction, configurationForMenuAtLocation location: CGPoint) -> UIContextMenuConfiguration? {
        guard let (hashtag, url) = getHashtagOrURL(),
              let navigationDelegate else {
            return nil
        }
        if let hashtag {
            return UIContextMenuConfiguration {
                HashtagTimelineViewController(for: hashtag, mastodonController: navigationDelegate.apiController)
            } actionProvider: { _ in
                UIMenu(children: self.actionsForHashtag(hashtag, source: .view(self)))
            }
        } else {
            return UIContextMenuConfiguration {
                let vc = SFSafariViewController(url: url)
                vc.preferredControlTintColor = Preferences.shared.accentColor.color
                return vc
            } actionProvider: { _ in
                UIMenu(children: self.actionsForURL(url, source: .view(self)))
            }
        }
    }
    
    
    func contextMenuInteraction(_ interaction: UIContextMenuInteraction, configuration: UIContextMenuConfiguration, highlightPreviewForItemWithIdentifier identifier: NSCopying) -> UITargetedPreview? {
        var rect = label.textRect(forBounds: label.bounds, limitedToNumberOfLines: 0)
        // the rect should be vertically centered, but textRect doesn't seem to take the label's vertical alignment into account
        rect.origin.x = 0
        rect.origin.y = (bounds.height - rect.height) / 2
        let parameters = UIPreviewParameters(textLineRects: [rect as NSValue])
        let preview = UITargetedPreview(view: label, parameters: parameters)
        currentTargetedPreview = preview
        return preview
    }
    
    func contextMenuInteraction(_ interaction: UIContextMenuInteraction, configuration: UIContextMenuConfiguration, dismissalPreviewForItemWithIdentifier identifier: NSCopying) -> UITargetedPreview? {
        return currentTargetedPreview
    }
    
    func contextMenuInteraction(_ interaction: UIContextMenuInteraction, willPerformPreviewActionForMenuWith configuration: UIContextMenuConfiguration, animator: UIContextMenuInteractionCommitAnimating) {
        MenuPreviewHelper.willPerformPreviewAction(animator: animator, presenter: navigationDelegate!)
    }
}
