//
// Copyright 2020 Signal Messenger, LLC
// SPDX-License-Identifier: AGPL-3.0-only
//

import SignalServiceKit
import SignalUI

public class CVComponentSticker: CVComponentBase, CVComponent {

    public var componentKey: CVComponentKey { .sticker }

    private let sticker: CVComponentState.Sticker
    private var stickerMetadata: (any StickerMetadata)? {
        sticker.stickerMetadata
    }
    private var attachmentStream: ReferencedTSResourceStream? {
        sticker.attachmentStream
    }
    private var attachmentPointer: ReferencedTSResourcePointer? {
        sticker.attachmentPointer
    }
    private var stickerInfo: StickerInfo? {
        stickerMetadata?.stickerInfo
    }

    init(itemModel: CVItemModel, sticker: CVComponentState.Sticker) {
        self.sticker = sticker

        super.init(itemModel: itemModel)
    }

    public func buildComponentView(componentDelegate: CVComponentDelegate) -> CVComponentView {
        CVComponentViewSticker()
    }

    public static let stickerSize: CGFloat = 175

    public func configureForRendering(componentView componentViewParam: CVComponentView,
                                      cellMeasurement: CVCellMeasurement,
                                      componentDelegate: CVComponentDelegate) {
        guard let componentView = componentViewParam as? CVComponentViewSticker else {
            owsFailDebug("Unexpected componentView.")
            componentViewParam.reset()
            return
        }

        let stackView = componentView.stackView

        switch sticker {
        case .available(_, let attachmentStream):
            let cacheKey = CVMediaCache.CacheKey.attachment(attachmentStream.attachment.resourceId)
            let isAnimated = attachmentStream.attachmentStream.computeContentType().isAnimatedImage
            let reusableMediaView: ReusableMediaView
            if let cachedView = mediaCache.getMediaView(cacheKey, isAnimated: isAnimated) {
                reusableMediaView = cachedView
            } else {
                let mediaViewAdapter = MediaViewAdapterSticker(attachmentStream: attachmentStream.attachmentStream)
                reusableMediaView = ReusableMediaView(mediaViewAdapter: mediaViewAdapter, mediaCache: mediaCache)
                mediaCache.setMediaView(reusableMediaView, forKey: cacheKey, isAnimated: isAnimated)
            }

            reusableMediaView.owner = componentView
            componentView.reusableMediaView = reusableMediaView
            let mediaView = reusableMediaView.mediaView

            stackView.reset()
            stackView.configure(config: stackViewConfig,
                                cellMeasurement: cellMeasurement,
                                measurementKey: Self.measurementKey_stackView,
                                subviews: [ mediaView ])

            switch CVAttachmentProgressView.progressType(
                forAttachment: .stream(attachmentStream),
                interaction: interaction
            ) {
            case .none:
                break
            case .uploading:
                let progressView = CVAttachmentProgressView(
                    direction: .upload(attachmentStream: attachmentStream.attachmentStream),
                    isDarkThemeEnabled: conversationStyle.isDarkThemeEnabled,
                    mediaCache: mediaCache
                )
                stackView.addSubview(progressView)
                stackView.centerSubviewOnSuperview(progressView, size: progressView.layoutSize)
            case .pendingDownload:
                break
            case .downloading:
                break
            case .unknown:
                owsFailDebug("Unknown progress type.")
            }
        case .downloading(let attachmentPointer):
            configureForRendering(
                attachmentPointer: attachmentPointer,
                transitTierDownloadState: .downloading,
                stackView: stackView,
                cellMeasurement: cellMeasurement
            )
        case .failedOrPending(let attachmentPointer, let transitTierDownloadState):
            configureForRendering(
                attachmentPointer: attachmentPointer,
                transitTierDownloadState: transitTierDownloadState,
                stackView: stackView,
                cellMeasurement: cellMeasurement
            )
        }
    }

    private func configureForRendering(
        attachmentPointer: ReferencedTSResourcePointer,
        transitTierDownloadState: AttachmentDownloadState,
        stackView: ManualStackView,
        cellMeasurement: CVCellMeasurement
    ) {
        let placeholderView = UIView()
        placeholderView.backgroundColor = Theme.secondaryBackgroundColor
        placeholderView.layer.cornerRadius = 18

        stackView.reset()
        stackView.configure(config: stackViewConfig,
                            cellMeasurement: cellMeasurement,
                            measurementKey: Self.measurementKey_stackView,
                            subviews: [ placeholderView ])

        let progressView = CVAttachmentProgressView(
            direction: .download(
                attachmentPointer: attachmentPointer.attachmentPointer,
                transitTierDownloadState: transitTierDownloadState
            ),
            isDarkThemeEnabled: conversationStyle.isDarkThemeEnabled,
            mediaCache: mediaCache
        )
        stackView.addSubview(progressView)
        stackView.centerSubviewOnSuperview(progressView, size: progressView.layoutSize)
    }

    private var stackViewConfig: CVStackViewConfig {
        CVStackViewConfig(axis: .vertical,
                          alignment: isOutgoing ? .trailing : .leading,
                          spacing: 0,
                          layoutMargins: .zero)
    }

    private static let measurementKey_stackView = "CVComponentSticker.measurementKey_stackView"

    public func measure(maxWidth: CGFloat, measurementBuilder: CVCellMeasurement.Builder) -> CGSize {
        owsAssertDebug(maxWidth > 0)

        let size: CGFloat = ceil(min(maxWidth, Self.stickerSize))
        let stickerSize = CGSize.square(size)
        let stickerInfo = stickerSize.asManualSubviewInfo(hasFixedSize: true)
        let stackMeasurement = ManualStackView.measure(config: stackViewConfig,
                                                       measurementBuilder: measurementBuilder,
                                                       measurementKey: Self.measurementKey_stackView,
                                                       subviewInfos: [ stickerInfo ],
                                                       maxWidth: maxWidth)
        return stackMeasurement.measuredSize
    }

    // MARK: - Events

    public override func handleTap(sender: UIGestureRecognizer,
                                   componentDelegate: CVComponentDelegate,
                                   componentView: CVComponentView,
                                   renderItem: CVRenderItem) -> Bool {

        guard let stickerMetadata = stickerMetadata,
              attachmentStream != nil else {
            // Not yet downloaded.
            return false
        }
        componentDelegate.didTapStickerPack(stickerMetadata.packInfo)
        return true
    }

    // MARK: -

    // Used for rendering some portion of an Conversation View item.
    // It could be the entire item or some part thereof.
    public class CVComponentViewSticker: NSObject, CVComponentView {

        fileprivate let stackView = ManualStackView(name: "sticker.container")

        fileprivate var reusableMediaView: ReusableMediaView?

        public var isDedicatedCellView = false

        public var rootView: UIView {
            stackView
        }

        public func setIsCellVisible(_ isCellVisible: Bool) {
            if isCellVisible {
                if let reusableMediaView = reusableMediaView,
                   reusableMediaView.owner == self {
                    reusableMediaView.load()
                }
            } else {
                if let reusableMediaView = reusableMediaView,
                   reusableMediaView.owner == self {
                    reusableMediaView.unload()
                }
            }
        }

        public func reset() {
            stackView.reset()

            if let reusableMediaView = reusableMediaView,
               reusableMediaView.owner == self {
                reusableMediaView.unload()
                reusableMediaView.owner = nil
            }
        }

    }
}

// MARK: -

extension CVComponentSticker: CVAccessibilityComponent {
    public var accessibilityDescription: String {
        if let approximateEmoji = stickerMetadata?.firstEmoji {
            return String(
                format: OWSLocalizedString(
                    "ACCESSIBILITY_LABEL_STICKER_FORMAT",
                    comment: "Accessibility label for stickers. Embeds {{ name of top emoji the sticker resembles }}"),
                approximateEmoji
            )
        } else {
            return OWSLocalizedString(
                "ACCESSIBILITY_LABEL_STICKER",
                comment: "Accessibility label for stickers."
            )
        }
    }
}
