import UIKit

public protocol CardContent {
    var view: UIView! { get }
    func contentHeight(forWidth: CGFloat) -> CGFloat
}

// Allows the card background view to communicate with the cell to detect taps in the title area
// A Random article card navigates to different destinations depending on whether the area
// above or below the card content is tapped.
fileprivate protocol CardBackgroundViewDelegate: UIView {
    func titleAreaYThreshold(for cardBackgroundView: CardBackgroundView) -> CGFloat // Return value in the coordinate system of the card background view
    var titleAreaTapped: Bool { get set }
}
private class CardBackgroundView: UIView {
    fileprivate weak var delegate: CardBackgroundViewDelegate?
    override public func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? {
        if let delegate = delegate {
            let yThreshold = delegate.titleAreaYThreshold(for: self)
            delegate.titleAreaTapped = point.y < yThreshold
        }
        return super.hitTest(point, with: event)
    }
}

public protocol ExploreCardCollectionViewCellDelegate: AnyObject {
    func exploreCardCollectionViewCellWantsCustomization(_ cell: ExploreCardCollectionViewCell)
    func exploreCardCollectionViewCellWantsToUndoCustomization(_ cell: ExploreCardCollectionViewCell)
}
    
public class ExploreCardCollectionViewCell: CollectionViewCell, CardBackgroundViewDelegate, Themeable {
    private let titleLabel = UILabel()
    private let subtitleLabel = UILabel()
    public let customizationButton = UIButton()
    private let undoButton = UIButton()
    private let undoLabel = UILabel()
    private let footerButton = AlignedImageButton()
    public weak var delegate: ExploreCardCollectionViewCellDelegate?
    private let cardBackgroundView = CardBackgroundView()
    private let cardCornerRadius = Theme.exploreCardCornerRadius
    private let cardShadowRadius = CGFloat(10)
    private let cardShadowOffset =  CGSize(width: 0, height: 2)
    public var titleAreaTapped: Bool = false

    static let overflowImage = UIImage(named: "overflow")
    
    public override func setup() {
        super.setup()
        titleLabel.numberOfLines = 0
        contentView.addSubview(titleLabel)
        subtitleLabel.numberOfLines = 0
        contentView.addSubview(subtitleLabel)
        customizationButton.setImage(ExploreCardCollectionViewCell.overflowImage, for: .normal)
        customizationButton.contentEdgeInsets = .zero
        customizationButton.imageEdgeInsets = .zero
        customizationButton.titleEdgeInsets = .zero
        customizationButton.titleLabel?.textAlignment = .center
        customizationButton.addTarget(self, action: #selector(customizationButtonPressed), for: .touchUpInside)
        cardBackgroundView.layer.cornerRadius = cardCornerRadius
        cardBackgroundView.layer.shadowOffset = cardShadowOffset
        cardBackgroundView.layer.shadowRadius = cardShadowRadius
        cardBackgroundView.layer.shadowColor = cardShadowColor.cgColor
        cardBackgroundView.layer.shadowOpacity = cardShadowOpacity
        cardBackgroundView.layer.masksToBounds = false
        cardBackgroundView.isOpaque = true
        cardBackgroundView.delegate = self
        contentView.addSubview(cardBackgroundView)
        contentView.addSubview(customizationButton)
        footerButton.imageIsRightAligned = true
        let image = #imageLiteral(resourceName: "places-more").imageFlippedForRightToLeftLayoutDirection()
        footerButton.setImage(image, for: .normal)
        footerButton.isUserInteractionEnabled = false
        footerButton.titleLabel?.numberOfLines = 0
        footerButton.titleLabel?.textAlignment = .right
        contentView.addSubview(footerButton)
        undoLabel.numberOfLines = 0
        contentView.addSubview(undoLabel)
        undoButton.titleLabel?.numberOfLines = 0
        undoButton.setTitle(CommonStrings.undo, for: .normal)
        undoButton.addTarget(self, action: #selector(undoButtonPressed), for: .touchUpInside)
        undoButton.isUserInteractionEnabled = true
        undoButton.titleLabel?.textAlignment = .right
        contentView.addSubview(undoButton)
    }

    // This method is called to reset the cell to the default configuration. It is called on initial setup and prepareForReuse. Subclassers should call super.
    override open func reset() {
        super.reset()
        layoutMargins = UIEdgeInsets(top: 15, left: 13, bottom: 15, right: 13)
        footerButton.isHidden = true
        undoButton.isHidden = true
        undoLabel.isHidden = true
    }
    
    public var cardContent: (CardContent & Themeable)? = nil {
        didSet {
            oldValue?.view?.removeFromSuperview()
            guard let view = cardContent?.view else {
                return
            }
            view.layer.cornerRadius = cardCornerRadius
            contentView.addSubview(view)
        }
    }
    
    fileprivate func titleAreaYThreshold(for cardBackgroundView: CardBackgroundView) -> CGFloat {
        // The title area is defined to include card background from its top down to the bottom of the card content
        // This registers taps on the side margins of the card content as in the title area
        let yThreshold = cardContent?.view?.frame.maxY ?? 0.0
        let convertedPoint = convert(CGPoint(x: 0.0, y: yThreshold), to: cardBackgroundView)
        return convertedPoint.y
    }

    private var undoTitle: String? {
        didSet {
            undoLabel.text = undoTitle
        }
    }
    
    public var footerTitle: String? {
        get {
            return footerButton.title(for: .normal)
        }
        set {
            footerButton.setTitle(newValue, for: .normal)
            footerButton.isHidden = newValue == nil
            setNeedsLayout()
        }
    }
    
    public var title: String? {
        get {
            return titleLabel.text
        }
        set {
            titleLabel.text = newValue
            setNeedsLayout()
        }
    }
    
    public var subtitle: String? {
        get {
            return subtitleLabel.text
        }
        set {
            subtitleLabel.text = newValue
            setNeedsLayout()
        }
    }
    
    public var isCustomizationButtonHidden: Bool {
        get {
            return customizationButton.isHidden
        }
        set {
            customizationButton.isHidden = newValue
            setNeedsLayout()
        }
    }

    public var undoType: WMFContentGroupUndoType = .none {
        didSet {
            switch undoType {
            case .contentGroup:
                undoTitle = WMFLocalizedString("explore-feed-preferences-card-hidden-title", value: "Card hidden", comment: "Title for button that appears in place of feed card hidden by user via the overflow button")
                isCollapsed = true
            case .contentGroupKind:
                guard let title = title else {
                    return
                }
                undoTitle = String.localizedStringWithFormat(WMFLocalizedString("explore-feed-preferences-feed-cards-hidden-title", value: "All %@ cards hidden", comment: "Title for cell that appears in place of feed card hidden by user via the overflow button - %@ is replaced with feed card type"), title)
                isCollapsed = true
            default:
               isCollapsed = false
            }
        }
    }

    private var isCollapsed: Bool = false {
        didSet {
            if isCollapsed {
                undoLabel.isHidden = false
                customizationButton.isHidden = true
                undoButton.isHidden = false
                cardContent?.view.isHidden = true
                titleLabel.isHidden = true
                subtitleLabel.isHidden = true
                footerButton.isHidden = true
                UIAccessibility.post(notification: UIAccessibility.Notification.layoutChanged, argument: undoButton)
            } else {
                cardContent?.view.isHidden = false
                undoLabel.isHidden = true
                undoButton.isHidden = true
                titleLabel.isHidden = title == nil
                subtitleLabel.isHidden = subtitle == nil
                footerButton.isHidden = footerTitle == nil
            }
            setNeedsLayout()
        }
    }
    
    override public func sizeThatFits(_ size: CGSize, apply: Bool) -> CGSize {
        let size = super.sizeThatFits(size, apply: apply) // intentionally shade size
        var origin = CGPoint(x: layoutMargins.left, y: layoutMargins.top)
        let widthMinusMargins = size.width - layoutMargins.left - layoutMargins.right
        let isRTL = traitCollection.layoutDirection == .rightToLeft
        let labelHorizontalAlignment: HorizontalAlignment = isRTL ? .right : .left
        let buttonHorizontalAlignment: HorizontalAlignment = isRTL ? .left : .right
        
        var customizationButtonDeltaWidthMinusMargins: CGFloat = 0
        if !customizationButton.isHidden {
            let customizationButtonSize = CGSize(width: 50, height: 50)
            let customizationButtonNudgeWidth = round(0.55 * customizationButtonSize.width)
            customizationButtonDeltaWidthMinusMargins = customizationButtonNudgeWidth
            if apply {
                let originX = isRTL ? layoutMargins.left - customizationButtonSize.width + customizationButtonNudgeWidth : size.width - layoutMargins.right - customizationButtonNudgeWidth
                let originY = origin.y - round(0.25 * customizationButtonSize.height)
                let customizationButtonOrigin = CGPoint(x: originX, y: originY)
                customizationButton.frame = CGRect(origin: customizationButtonOrigin, size: customizationButtonSize)
            }
        }
        
        var labelOrigin = origin
        if isRTL {
            labelOrigin.x += customizationButtonDeltaWidthMinusMargins
        }

        if !titleLabel.isHidden {
            origin.y += titleLabel.wmf_preferredHeight(at: labelOrigin, maximumWidth: widthMinusMargins - customizationButtonDeltaWidthMinusMargins, horizontalAlignment: labelHorizontalAlignment, spacing: 4, apply: apply)
            labelOrigin.y = origin.y
        }
        if !subtitleLabel.isHidden {
            origin.y += subtitleLabel.wmf_preferredHeight(at: labelOrigin, maximumWidth: widthMinusMargins - customizationButtonDeltaWidthMinusMargins, horizontalAlignment: labelHorizontalAlignment, spacing: 20, apply: apply)
        }

        if let cardContent = cardContent, !cardContent.view.isHidden {
            let view = cardContent.view
            let height = cardContent.contentHeight(forWidth: widthMinusMargins)
            let cardContentViewFrame = CGRect(origin: origin, size: CGSize(width: widthMinusMargins, height: height))
            if apply {
                view?.frame = cardContentViewFrame
                cardBackgroundView.frame = cardContentViewFrame.insetBy(dx: -cardBorderWidth, dy: -cardBorderWidth)
            }
            origin.y += cardContentViewFrame.height
        }

        if isCollapsed, !undoLabel.isHidden, !undoButton.isHidden {
            let undoOffset: UIOffset = UIOffset(horizontal: 15, vertical: 16)
            labelOrigin.x += undoOffset.horizontal
            labelOrigin.y += undoOffset.vertical

            let undoButtonMaxWidthPercentage: CGFloat = 0.25

            let undoLabelMaxWidth = widthMinusMargins - (widthMinusMargins * undoButtonMaxWidthPercentage)
            let undoLabelMinWidth = widthMinusMargins * 0.5
            let undoLabelX = isRTL ? widthMinusMargins - undoLabelMaxWidth : labelOrigin.x
            let undoLabelFrameHeight = undoLabel.wmf_preferredHeight(at: CGPoint(x: undoLabelX, y: labelOrigin.y), maximumWidth: undoLabelMaxWidth, minimumWidth: undoLabelMinWidth, horizontalAlignment: labelHorizontalAlignment, spacing: 0, apply: apply)

            let undoButtonMaxWidth = widthMinusMargins * undoButtonMaxWidthPercentage
            let undoButtonX = isRTL ? labelOrigin.x : widthMinusMargins - undoButtonMaxWidth
            let undoButtonMinSize = CGSize(width: UIView.noIntrinsicMetric, height: undoLabelFrameHeight)
            let undoButtonMaxSize = CGSize(width: undoButtonMaxWidth, height: UIView.noIntrinsicMetric)
            let undoButtonFrame = undoButton.wmf_preferredFrame(at: CGPoint(x: undoButtonX, y: labelOrigin.y), maximumSize: undoButtonMaxSize, minimumSize: undoButtonMinSize, horizontalAlignment: buttonHorizontalAlignment, apply: apply)
            let undoHeight = max(undoLabelFrameHeight, undoButtonFrame.height)
            
            // If cardBackgroundView metrics change double check the hitTest() override in CardBackgroundView
            let cardBackgroundViewHeight = undoHeight + undoOffset.vertical * 2
            let cardBackgroundViewFrame = CGRect(x: layoutMargins.left, y: layoutMargins.top, width: widthMinusMargins, height: cardBackgroundViewHeight)
            if apply {
                cardBackgroundView.frame = cardBackgroundViewFrame
            }

            origin.y += cardBackgroundViewFrame.height
        }
    
        if !footerButton.isHidden {
            origin.y += layoutMargins.bottom
            origin.y += footerButton.wmf_preferredHeight(at: origin, maximumWidth: widthMinusMargins, horizontalAlignment: buttonHorizontalAlignment, spacing: 0, apply: apply)
        }

        origin.y += layoutMargins.bottom
        
        let totalSize = CGSize(width: size.width, height: ceil(origin.y))
        
        if apply {
            cardBackgroundView.layer.shadowPath = UIBezierPath(roundedRect: cardBackgroundView.bounds, cornerRadius: cardBackgroundView.layer.cornerRadius).cgPath
        }

        return totalSize
    }
    
    public override func updateFonts(with traitCollection: UITraitCollection) {
        super.updateFonts(with: traitCollection)
        titleLabel.font = UIFont.wmf_font(.semiboldSubheadline, compatibleWithTraitCollection: traitCollection)
        subtitleLabel.font = UIFont.wmf_font(.subheadline, compatibleWithTraitCollection: traitCollection)
        footerButton.titleLabel?.font = UIFont.wmf_font(.semiboldSubheadline, compatibleWithTraitCollection: traitCollection)
        undoLabel.font = UIFont.wmf_font(.subheadline, compatibleWithTraitCollection: traitCollection)
        undoButton.titleLabel?.font = UIFont.wmf_font(.semiboldSubheadline, compatibleWithTraitCollection: traitCollection)
        customizationButton.titleLabel?.font = UIFont.wmf_font(.boldTitle1, compatibleWithTraitCollection: traitCollection)
    }
    
    private var cardShadowColor: UIColor = .black {
        didSet {
            cardBackgroundView.layer.shadowColor = cardShadowColor.cgColor
        }
    }
    
    private var cardShadowOpacity: Float = 0 {
        didSet {
            guard cardBackgroundView.layer.shadowOpacity != cardShadowOpacity else {
                return
            }
            cardBackgroundView.layer.shadowOpacity = cardShadowOpacity
        }
    }
    
    private var cardBorderWidth: CGFloat = 1 {
        didSet {
            cardBackgroundView.layer.borderWidth = cardBorderWidth
        }
    }
    
    public override func updateBackgroundColorOfLabels() {
        super.updateBackgroundColorOfLabels()
        titleLabel.backgroundColor = labelBackgroundColor
        subtitleLabel.backgroundColor = labelBackgroundColor
        footerButton.backgroundColor = labelBackgroundColor
        undoLabel.backgroundColor = labelBackgroundColor
        undoButton.backgroundColor = labelBackgroundColor
        customizationButton.backgroundColor = labelBackgroundColor
    }
    
    public func apply(theme: Theme) {
        contentView.tintColor = theme.colors.link
        let backgroundColor = isCollapsed ? theme.colors.cardButtonBackground : theme.colors.paperBackground
        let selectedBackgroundColor = isCollapsed ? theme.colors.cardButtonBackground : theme.colors.midBackground
        let cardBackgroundViewBorderColor = isCollapsed ? backgroundColor.cgColor : theme.colors.cardBorder.cgColor
        cardBackgroundView.layer.borderColor = cardBackgroundViewBorderColor
        setBackgroundColors(.clear, selected: selectedBackgroundColor)
        titleLabel.textColor = theme.colors.primaryText
        subtitleLabel.textColor = theme.colors.secondaryText
        customizationButton.setTitleColor(theme.colors.link, for: .normal)
        footerButton.setTitleColor(theme.colors.link, for: .normal)
        undoLabel.textColor = theme.colors.primaryText
        undoButton.setTitleColor(theme.colors.link, for: .normal)
        updateSelectedOrHighlighted()
        cardBackgroundView.backgroundColor = backgroundColor
        cardShadowOpacity = theme.cardShadowOpacity
        cardShadowColor = theme.colors.cardShadow
        cardContent?.apply(theme: theme)
        let displayScale = max(1, traitCollection.displayScale)
        cardBorderWidth = CGFloat(theme.cardBorderWidthInPixels) / displayScale
    }
    
    @objc func customizationButtonPressed() {
        delegate?.exploreCardCollectionViewCellWantsCustomization(self)
    }

    @objc func undoButtonPressed() {
        delegate?.exploreCardCollectionViewCellWantsToUndoCustomization(self)
    }
    
    // MARK: - Accessibility
    
    override open func updateAccessibilityElements() {
        var updatedAccessibilityElements: [Any] = []

        if isCollapsed {
            updatedAccessibilityElements.append(undoLabel)
            updatedAccessibilityElements.append(undoButton)
        } else {
            let groupedLabels = [titleLabel, subtitleLabel]
            let customizeActionTitle = WMFLocalizedString("explore-feed-customize-accessibility-title", value: "Customize", comment: "Accessibility title for feed customization")
            let customizeAction = UIAccessibilityCustomAction(name: customizeActionTitle, target: self, selector: #selector(customizationButtonPressed))
            updatedAccessibilityElements.append(LabelGroupAccessibilityElement(view: self, labels: groupedLabels, actions: [customizeAction]))
            if let contentView = cardContent?.view {
                updatedAccessibilityElements.append(contentView)
            }
            if !footerButton.isHidden, let label = footerButton.titleLabel {
                let footerElement = UIAccessibilityElement(accessibilityContainer: self)
                footerElement.isAccessibilityElement = true
                footerElement.accessibilityLabel = label.text
                footerElement.accessibilityTraits = UIAccessibilityTraits.link
                footerElement.accessibilityFrameInContainerSpace = footerButton.frame
                updatedAccessibilityElements.append(footerElement)
            }
        }
        
        accessibilityElements = updatedAccessibilityElements
    }
}
