//
//  StatusContentContainer.swift
//  Tusker
//
//  Created by Shadowfacts on 10/2/22.
//  Copyright © 2022 Shadowfacts. All rights reserved.
//

import UIKit

protocol StatusContentPollView: UIView {
    func estimateHeight(effectiveWidth: CGFloat) -> CGFloat
}

class StatusContentContainer<ContentView: ContentTextView, PollView: StatusContentPollView>: UIView {
    
    private var useTopSpacer = false
    private let topSpacer = UIView().configure {
        $0.backgroundColor = .clear
        // other 4pt is provided by this view's own spacing
        $0.heightAnchor.constraint(equalToConstant: 4).isActive = true
    }

    let contentTextView = ContentView().configure {
         $0.adjustsFontForContentSizeCategory = true
         $0.isScrollEnabled = false
         $0.backgroundColor = nil
         $0.isEditable = false
         $0.isSelectable = false
    }
    
    private static var cardViewHeight: CGFloat { 90 }
    let cardView = StatusCardView().configure {
        NSLayoutConstraint.activate([
            $0.heightAnchor.constraint(equalToConstant: StatusContentContainer.cardViewHeight),
        ])
    }
    
    let attachmentsView = AttachmentsContainerView()
    
    let pollView = PollView()
    
    private var arrangedSubviews: [UIView] {
        if useTopSpacer {
            return [topSpacer, contentTextView, cardView, attachmentsView, pollView]
        } else {
            return [contentTextView, cardView, attachmentsView, pollView]
        }
    }
    
    private var isHiddenObservations: [NSKeyValueObservation] = []
    
    private var visibleSubviews = IndexSet()
    private var verticalConstraints: [NSLayoutConstraint] = []
    private var lastSubviewBottomConstraint: (UIView, NSLayoutConstraint)?
    private var zeroHeightConstraint: NSLayoutConstraint!
    
    private var isCollapsed = false
    
    var visibleSubviewHeight: CGFloat {
        subviews.filter { !$0.isHidden }.map(\.bounds.height).reduce(0, +)
    }
   
    init(useTopSpacer: Bool) {
        self.useTopSpacer = useTopSpacer
        
        super.init(frame: .zero)
        
        for subview in arrangedSubviews {
            subview.translatesAutoresizingMaskIntoConstraints = false
            addSubview(subview)
            
            NSLayoutConstraint.activate([
                subview.leadingAnchor.constraint(equalTo: leadingAnchor),
                subview.trailingAnchor.constraint(equalTo: trailingAnchor),
            ])
        }
        
        // this constraint needs to have low priority so that during the collapse/expand animation, the content container is the view that shrinks/expands
        zeroHeightConstraint = heightAnchor.constraint(equalToConstant: 0)
        zeroHeightConstraint.priority = .defaultLow
        
        setNeedsUpdateConstraints()
                
        isHiddenObservations = arrangedSubviews.map {
            $0.observe(\.isHidden) { [unowned self] _, _ in
                self.setNeedsUpdateConstraints()
            }
        }
    }
    
    required init?(coder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
    
    override func updateConstraints() {
        let visibleSubviews = IndexSet(arrangedSubviews.indices.filter { !arrangedSubviews[$0].isHidden })
        if self.visibleSubviews != visibleSubviews {
            self.visibleSubviews = visibleSubviews
            NSLayoutConstraint.deactivate(verticalConstraints)
            verticalConstraints = []
            var lastVisibleSubview: UIView?
            
            for subviewIndex in visibleSubviews {
                let subview = arrangedSubviews[subviewIndex]
                if let lastVisibleSubview {
                    verticalConstraints.append(subview.topAnchor.constraint(equalTo: lastVisibleSubview.bottomAnchor, constant: 4))
                } else {
                    verticalConstraints.append(subview.topAnchor.constraint(equalTo: topAnchor))
                }
                lastVisibleSubview = subview
            }
            
            NSLayoutConstraint.activate(verticalConstraints)
        }
            
        if lastSubviewBottomConstraint == nil || arrangedSubviews[visibleSubviews.last!] !== lastSubviewBottomConstraint?.0 {
            lastSubviewBottomConstraint?.1.isActive = false
            // this constraint needs to have low priority so that during the collapse/expand animation, the content container is the view that shrinks/expands
            let lastVisibleSubview = arrangedSubviews[visibleSubviews.last!]
            let constraint = lastVisibleSubview.bottomAnchor.constraint(equalTo: bottomAnchor)
            constraint.isActive = !isCollapsed
            constraint.priority = .defaultLow
            lastSubviewBottomConstraint = (lastVisibleSubview, constraint)
        }
        
        zeroHeightConstraint.isActive = isCollapsed
        
        super.updateConstraints()
    }
    
    func setCollapsed(_ collapsed: Bool) {
        guard collapsed != isCollapsed else {
            return
        }
        isCollapsed = collapsed
        
        // don't call setNeedsUpdateConstraints b/c that destroys/recreates a bunch of other constraints
        // if there is no lastSubviewBottomConstraint, then we already need a constraint update, so we don't need to do anything here
        if let lastSubviewBottomConstraint {
            lastSubviewBottomConstraint.1.isActive = !collapsed
            zeroHeightConstraint.isActive = collapsed
        }
    }
    
    // used only for collapsing automatically based on height, doesn't need to be accurate
    // just roughly inline with the content height
    func estimateVisibleSubviewHeight(effectiveWidth: CGFloat) -> CGFloat {
        var height: CGFloat = 0
        height += contentTextView.sizeThatFits(CGSize(width: effectiveWidth, height: UIView.layoutFittingCompressedSize.height)).height
        if !cardView.isHidden {
            height += StatusContentContainer.cardViewHeight
        }
        if !attachmentsView.isHidden {
            height += effectiveWidth / attachmentsView.aspectRatio
        }
        if !pollView.isHidden {
            let pollHeight = pollView.estimateHeight(effectiveWidth: effectiveWidth)
            height += pollHeight
        }
        return height
    }
    
}
