//
//  StatusCardView.swift
//  Tusker
//
//  Created by Shadowfacts on 10/25/20.
//  Copyright © 2020 Shadowfacts. All rights reserved.
//

import UIKit
import Pachyderm
import SafariServices
import WebURLFoundationExtras
import SwiftSoup

class StatusCardView: UIView {
    
    weak var navigationDelegate: TuskerNavigationDelegate?
    weak var actionProvider: MenuActionProvider?
    
    private var statusID: String?
    private(set) var card: Card?
    
    private let activeBackgroundColor = UIColor.secondarySystemFill
    private let inactiveBackgroundColor = UIColor.secondarySystemBackground
    
    private var isGrayscale = false
    
    private var hStack: UIStackView!
    private var vStack: UIStackView!
    private var titleLabel: UILabel!
    private var descriptionLabel: UILabel!
    private var domainLabel: UILabel!
    private var imageView: StatusCardImageView!
    private var placeholderImageView: UIImageView!
    private var leadingSpacer: UIView!
    private var trailingSpacer: UIView!
    
    override init(frame: CGRect) {
        super.init(frame: frame)
        commonInit()
    }
    
    required init?(coder: NSCoder) {
        super.init(coder: coder)
        commonInit()
    }
    
    private func commonInit() {
        self.layer.shadowColor = UIColor.black.cgColor
        self.layer.shadowRadius = 5
        self.layer.shadowOpacity = 0.2
        self.layer.shadowOffset = .zero
        
        self.addInteraction(UIContextMenuInteraction(delegate: self))
        
        titleLabel = UILabel()
        titleLabel.font = UIFont(descriptor: UIFontDescriptor.preferredFontDescriptor(withTextStyle: .subheadline).withSymbolicTraits(.traitBold)!, size: 0)
        titleLabel.adjustsFontForContentSizeCategory = true
        titleLabel.numberOfLines = 2
        titleLabel.setContentHuggingPriority(.defaultHigh, for: .vertical)
        
        descriptionLabel = UILabel()
        descriptionLabel.font = UIFont(descriptor: .preferredFontDescriptor(withTextStyle: .caption1), size: 0)
        descriptionLabel.adjustsFontForContentSizeCategory = true
        descriptionLabel.numberOfLines = 3
        descriptionLabel.setContentCompressionResistancePriority(.defaultLow, for: .vertical)
        
        domainLabel = UILabel()
        domainLabel.font = .preferredFont(forTextStyle: .caption2)
        domainLabel.adjustsFontForContentSizeCategory = true
        domainLabel.numberOfLines = 1
        domainLabel.textColor = .tintColor
        
        vStack = UIStackView(arrangedSubviews: [
            titleLabel,
            descriptionLabel,
            domainLabel,
        ])
        vStack.axis = .vertical
        vStack.alignment = .leading
        vStack.spacing = 0
        
        imageView = StatusCardImageView(cache: .attachments)
        imageView.contentMode = .scaleAspectFill
        imageView.clipsToBounds = true
        
        leadingSpacer = UIView()
        leadingSpacer.backgroundColor = .clear
        trailingSpacer = UIView()
        trailingSpacer.backgroundColor = .clear
        
        hStack = UIStackView(arrangedSubviews: [
            leadingSpacer,
            imageView,
            vStack,
            trailingSpacer,
        ])
        hStack.translatesAutoresizingMaskIntoConstraints = false
        hStack.axis = .horizontal
        hStack.alignment = .center
        hStack.distribution = .fill
        hStack.spacing = 4
        hStack.clipsToBounds = true
        hStack.layer.borderWidth = 0.5
        hStack.layer.cornerCurve = .continuous
        hStack.backgroundColor = inactiveBackgroundColor
        updateBorderColor()
        
        addSubview(hStack)
        
        placeholderImageView = UIImageView(image: UIImage(systemName: "doc.text"))
        placeholderImageView.translatesAutoresizingMaskIntoConstraints = false
        placeholderImageView.contentMode = .scaleAspectFit
        placeholderImageView.tintColor = .gray
        placeholderImageView.isHidden = true

        addSubview(placeholderImageView)
        
        NSLayoutConstraint.activate([
            imageView.heightAnchor.constraint(equalTo: heightAnchor),
            imageView.widthAnchor.constraint(equalTo: imageView.heightAnchor),
            
            vStack.heightAnchor.constraint(lessThanOrEqualTo: heightAnchor, constant: -8),
            
            leadingSpacer.widthAnchor.constraint(equalToConstant: 4),
            trailingSpacer.widthAnchor.constraint(equalToConstant: 4),
            
            hStack.leadingAnchor.constraint(equalTo: leadingAnchor),
            hStack.trailingAnchor.constraint(equalTo: trailingAnchor),
            hStack.topAnchor.constraint(equalTo: topAnchor),
            hStack.bottomAnchor.constraint(equalTo: bottomAnchor),
            
            placeholderImageView.widthAnchor.constraint(equalToConstant: 30),
            placeholderImageView.heightAnchor.constraint(equalToConstant: 30),
            placeholderImageView.centerXAnchor.constraint(equalTo: imageView.centerXAnchor),
            placeholderImageView.centerYAnchor.constraint(equalTo: imageView.centerYAnchor),
        ])
    }
    
    override func traitCollectionDidChange(_ previousTraitCollection: UITraitCollection?) {
        super.traitCollectionDidChange(previousTraitCollection)
        updateBorderColor()
    }
    
    override func layoutSubviews() {
        super.layoutSubviews()
        hStack.layer.cornerRadius = 0.1 * bounds.height
    }
    
    private func updateBorderColor() {
        if traitCollection.userInterfaceStyle == .dark {
            hStack.layer.borderColor = UIColor.darkGray.cgColor
        } else {
            hStack.layer.borderColor = UIColor.lightGray.cgColor
        }
    }
    
    func updateUI(status: StatusMO) {
        guard status.id != statusID else {
            return
        }
        self.card = status.card
        self.statusID = status.id
        
        guard let card = status.card else {
            return
        }
        
        if let image = card.image {
            if status.sensitive {
                if let blurhash = card.blurhash {
                    imageView.blurImage = false
                    imageView.showOnlyBlurHash(blurhash, for: URL(image)!)
                } else {
                    // if we don't have a blurhash, load the image and show it behind a blur
                    imageView.blurImage = true
                    imageView.update(for: URL(image), blurhash: nil)
                }
            } else {
                imageView.update(for: URL(image), blurhash: card.blurhash)
            }
            imageView.isHidden = false
            leadingSpacer.isHidden = true
        } else {
            imageView.update(for: nil)
            imageView.isHidden = true
            leadingSpacer.isHidden = false
        }
        
        let title = card.title.trimmingCharacters(in: .whitespacesAndNewlines)
        titleLabel.text = title
        titleLabel.isHidden = title.isEmpty
        
        let description = try! SwiftSoup.parseBodyFragment(card.description).text()
        descriptionLabel.text = description
        descriptionLabel.isHidden = description.isEmpty
        
        if let host = card.url.host {
            domainLabel.text = host.serialized
            domainLabel.isHidden = false
        } else {
            domainLabel.isHidden = true
        }
        
        let titleHeight = titleLabel.isHidden ? 0 : titleLabel.sizeThatFits(CGSize(width: titleLabel.bounds.width, height: UIView.layoutFittingCompressedSize.height)).height
        let descriptionHeight = descriptionLabel.isHidden ? 0 : descriptionLabel.sizeThatFits(CGSize(width: descriptionLabel.bounds.width, height: UIView.layoutFittingCompressedSize.height)).height
        let domainLabel = domainLabel.isHidden ? 0 : domainLabel.sizeThatFits(CGSize(width: domainLabel.bounds.width, height: UIView.layoutFittingCompressedSize.height)).height
        if titleHeight + descriptionHeight + domainLabel > vStack.bounds.height {
            descriptionLabel.isHidden = true
        }
    }
    
    override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
        hStack.backgroundColor = activeBackgroundColor
        setNeedsDisplay()
    }
    
    override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) {
    }
    
    override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) {
        hStack.backgroundColor = inactiveBackgroundColor
        setNeedsDisplay()
        
        if let card = card, let delegate = navigationDelegate {
            delegate.selected(url: URL(card.url)!)
        }
    }
    
    override func touchesCancelled(_ touches: Set<UITouch>, with event: UIEvent?) {
        hStack.backgroundColor = inactiveBackgroundColor
        setNeedsDisplay()
    }

}

extension StatusCardView: UIContextMenuInteractionDelegate {
    func contextMenuInteraction(_ interaction: UIContextMenuInteraction, configurationForMenuAtLocation location: CGPoint) -> UIContextMenuConfiguration? {
        guard let card = card else { return nil }
        
        return UIContextMenuConfiguration(identifier: nil) {
            let vc = SFSafariViewController(url: URL(card.url)!)
            vc.preferredControlTintColor = Preferences.shared.accentColor.color
            return vc
        } actionProvider: { (_) in
            let actions = self.actionProvider?.actionsForURL(URL(card.url)!, source: .view(self)) ?? []
            return UIMenu(title: "", image: nil, identifier: nil, options: [], children: actions)
        }
    }
    
    func contextMenuInteraction(_ interaction: UIContextMenuInteraction, previewForHighlightingMenuWithConfiguration configuration: UIContextMenuConfiguration) -> UITargetedPreview? {
        let params = UIPreviewParameters()
        params.visiblePath = UIBezierPath(roundedRect: hStack.bounds, cornerRadius: hStack.layer.cornerRadius)
        return UITargetedPreview(view: hStack, parameters: params)
    }
    
    func contextMenuInteraction(_ interaction: UIContextMenuInteraction, willPerformPreviewActionForMenuWith configuration: UIContextMenuConfiguration, animator: UIContextMenuInteractionCommitAnimating) {
        if let viewController = animator.previewViewController,
           let delegate = navigationDelegate {
            animator.preferredCommitStyle = .pop
            animator.addCompletion {
                if let customPresenting = viewController as? CustomPreviewPresenting {
                    customPresenting.presentFromPreview(presenter: delegate)
                } else {
                    delegate.show(viewController)
                }
            }
        }
    }
}

private class StatusCardImageView: CachedImageView {
    @Lazy private var blurView = UIVisualEffectView(effect: UIBlurEffect(style: .dark))
    var blurImage = false {
        didSet {
            if blurImage {
                if !_blurView.isInitialized {
                    blurView.translatesAutoresizingMaskIntoConstraints = false
                    addSubview(blurView)
                    NSLayoutConstraint.activate([
                        blurView.leadingAnchor.constraint(equalTo: leadingAnchor),
                        blurView.trailingAnchor.constraint(equalTo: trailingAnchor),
                        blurView.topAnchor.constraint(equalTo: topAnchor),
                        blurView.bottomAnchor.constraint(equalTo: bottomAnchor),
                    ])
                }
            } else {
                _blurView.valueIfInitialized?.removeFromSuperview()
            }
        }
    }
}
