// Copyright © 2022 Rangeproof Pty Ltd. All rights reserved.

import UIKit
import NVActivityIndicatorView
import SessionUIKit
import SessionMessagingKit

final class LinkPreviewView: UIView {
    private static let loaderSize: CGFloat = 24
    private static let cancelButtonSize: CGFloat = 45
    
    private let maxWidth: CGFloat
    private let onCancel: (() -> ())?

    // MARK: - UI
    
    private lazy var imageViewContainerWidthConstraint = imageView.set(.width, to: 100)
    private lazy var imageViewContainerHeightConstraint = imageView.set(.height, to: 100)

    // MARK: UI Components

    private lazy var imageView: UIImageView = {
        let result: UIImageView = UIImageView()
        result.contentMode = .scaleAspectFill
        
        return result
    }()

    private lazy var imageViewContainer: UIView = {
        let result: UIView = UIView()
        result.clipsToBounds = true
        
        return result
    }()

    private let loader: NVActivityIndicatorView = {
        let result: NVActivityIndicatorView = NVActivityIndicatorView(
            frame: CGRect.zero,
            type: .circleStrokeSpin,
            color: .black,
            padding: nil
        )
        
        ThemeManager.onThemeChange(observer: result) { [weak result] theme, _ in
            guard let textPrimary: UIColor = theme.color(for: .textPrimary) else { return }
            
            result?.color = textPrimary
        }
        
        return result
    }()

    private lazy var titleLabel: UILabel = {
        let result: UILabel = UILabel()
        result.font = .boldSystemFont(ofSize: Values.smallFontSize)
        result.numberOfLines = 0
        
        return result
    }()

    private lazy var bodyTappableLabelContainer: UIView = UIView()

    private lazy var hStackViewContainer: UIView = UIView()

    private lazy var hStackView: UIStackView = UIStackView()

    private lazy var cancelButton: UIButton = {
        let result: UIButton = UIButton(type: .custom)
        result.setImage(
            UIImage(named: "X")?
                .withRenderingMode(.alwaysTemplate),
            for: .normal
        )
        result.themeTintColor = .textPrimary
        
        let cancelButtonSize = LinkPreviewView.cancelButtonSize
        result.set(.width, to: cancelButtonSize)
        result.set(.height, to: cancelButtonSize)
        result.addTarget(self, action: #selector(cancel), for: UIControl.Event.touchUpInside)
        
        return result
    }()
    
    var bodyTappableLabel: TappableLabel?

    // MARK: - Initialization
    
    init(maxWidth: CGFloat, onCancel: (() -> ())? = nil) {
        self.maxWidth = maxWidth
        self.onCancel = onCancel
        
        super.init(frame: CGRect.zero)
        
        setUpViewHierarchy()
    }

    override init(frame: CGRect) {
        preconditionFailure("Use init(for:maxWidth:delegate:) instead.")
    }

    required init?(coder: NSCoder) {
        preconditionFailure("Use init(for:maxWidth:delegate:) instead.")
    }

    private func setUpViewHierarchy() {
        // Image view
        imageViewContainerWidthConstraint.isActive = true
        imageViewContainerHeightConstraint.isActive = true
        imageViewContainer.addSubview(imageView)
        imageView.pin(to: imageViewContainer)
        
        // Title label
        let titleLabelContainer = UIView()
        titleLabelContainer.addSubview(titleLabel)
        titleLabel.pin(to: titleLabelContainer, withInset: Values.mediumSpacing)
        
        // Horizontal stack view
        hStackView.addArrangedSubview(imageViewContainer)
        hStackView.addArrangedSubview(titleLabelContainer)
        hStackView.axis = .horizontal
        hStackView.alignment = .center
        hStackViewContainer.addSubview(hStackView)
        hStackView.pin(to: hStackViewContainer)
        
        // Vertical stack view
        let vStackView = UIStackView(arrangedSubviews: [ hStackViewContainer, bodyTappableLabelContainer ])
        vStackView.axis = .vertical
        addSubview(vStackView)
        vStackView.pin(to: self)
        
        // Loader
        addSubview(loader)
        
        let loaderSize = LinkPreviewView.loaderSize
        loader.set(.width, to: loaderSize)
        loader.set(.height, to: loaderSize)
        loader.center(in: self)
    }

    // MARK: - Updating
    
    public func update(
        with state: LinkPreviewState,
        isOutgoing: Bool,
        delegate: TappableLabelDelegate? = nil,
        cellViewModel: MessageViewModel? = nil,
        bodyLabelTextColor: ThemeValue? = nil,
        lastSearchText: String? = nil
    ) {
        cancelButton.removeFromSuperview()
        
        var image: UIImage? = state.image
        let stateHasImage: Bool = (image != nil)
        if image == nil && (state is LinkPreview.DraftState || state is LinkPreview.SentState) {
            image = UIImage(named: "Link")?.withRenderingMode(.alwaysTemplate)
        }
        
        // Image view
        let imageViewContainerSize: CGFloat = (state is LinkPreview.SentState ? 100 : 80)
        imageViewContainerWidthConstraint.constant = imageViewContainerSize
        imageViewContainerHeightConstraint.constant = imageViewContainerSize
        imageViewContainer.layer.cornerRadius = (state is LinkPreview.SentState ? 0 : 8)
        
        imageView.image = image
        imageView.themeTintColor = (isOutgoing ?
            .messageBubble_outgoingText :
            .messageBubble_incomingText
        )
        imageView.contentMode = (stateHasImage ? .scaleAspectFill : .center)
        
        // Loader
        loader.alpha = (image != nil ? 0 : 1)
        if image != nil { loader.stopAnimating() } else { loader.startAnimating() }
        
        // Title
        titleLabel.text = state.title
        titleLabel.themeTextColor = (isOutgoing ?
            .messageBubble_outgoingText :
            .messageBubble_incomingText
        )
        
        // Horizontal stack view
        switch state {
            case is LinkPreview.LoadingState:
                imageViewContainer.themeBackgroundColor = .clear
                hStackViewContainer.themeBackgroundColor = nil
                
            case is LinkPreview.SentState:
                imageViewContainer.themeBackgroundColor = .messageBubble_overlay
                hStackViewContainer.themeBackgroundColor = .messageBubble_overlay
                
            default:
                imageViewContainer.themeBackgroundColor = .messageBubble_overlay
                hStackViewContainer.themeBackgroundColor = nil
        }
        
        // Body text view
        bodyTappableLabelContainer.subviews.forEach { $0.removeFromSuperview() }
        
        if let cellViewModel: MessageViewModel = cellViewModel {
            let bodyTappableLabel = VisibleMessageCell.getBodyTappableLabel(
                for: cellViewModel,
                with: maxWidth,
                textColor: (bodyLabelTextColor ?? .textPrimary),
                searchText: lastSearchText,
                delegate: delegate
            )
            
            self.bodyTappableLabel = bodyTappableLabel
            bodyTappableLabelContainer.addSubview(bodyTappableLabel)
            bodyTappableLabel.pin(to: bodyTappableLabelContainer, withInset: 12)
        }
        
        if state is LinkPreview.DraftState {
            hStackView.addArrangedSubview(cancelButton)
        }
    }

    // MARK: - Interaction
    
    @objc private func cancel() {
        onCancel?()
    }
}