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

import UIKit
import SessionUIKit

final class SessionLabelCarouselView: UIView, UIScrollViewDelegate {
    private static let autoScrollingTimeInterval: TimeInterval = 10
    private var labelInfos: [LabelInfo] = []
    private var labelSize: CGSize = .zero
    private var shouldAutoScroll: Bool = false
    private var timer: Timer?
    
    private lazy var contentWidth = stackView.set(.width, to: 0)
    private lazy var contentHeight = stackView.set(.height, to: 0)
    
    private var shouldScroll: Bool = false {
        didSet {
            arrowLeft.isHidden = !shouldScroll
            arrowRight.isHidden = !shouldScroll
            pageControl.isHidden = !shouldScroll
        }
    }
    
    public struct LabelInfo {
        let attributedText: NSAttributedString
        let accessibility: Accessibility?
        let type: LabelType
    }
    
    // MARK: - Types
    
    public enum LabelType {
        case notificationSettings
        case userCount
        case disappearingMessageSetting
    }
    
    public var currentLabelType: LabelType? {
        let index = pageControl.currentPage + (shouldScroll ? 1 : 0)
        return self.labelInfos[safe: index]?.type
    }
    
    // MARK: - UI Components
    
    public lazy var scrollView: UIScrollView = {
        let result = UIScrollView(frame: .zero)
        result.isPagingEnabled = true
        result.showsVerticalScrollIndicator = false
        result.showsHorizontalScrollIndicator = false
        result.delegate = self
        
        return result
    }()
    
    private lazy var stackView: UIStackView = {
        let result = UIStackView()
        result.axis = .horizontal
        
        return result
    }()
    
    private lazy var pageControl: UIPageControl = {
        let result = UIPageControl(frame: .zero)
        result.themeCurrentPageIndicatorTintColor = .textPrimary
        result.themePageIndicatorTintColor = .textSecondary
        result.themeTintColor = .textPrimary
        result.currentPage = 0
        result.set(.height, to: 5)
        result.transform = CGAffineTransform(scaleX: 0.5, y: 0.5)
        return result
    }()
    
    private lazy var arrowLeft: UIImageView = {
        let result = UIImageView(image: UIImage(systemName: "chevron.left")?.withRenderingMode(.alwaysTemplate))
        result.themeTintColor = .textPrimary
        result.set(.height, to: 10)
        result.set(.width, to: 5)
        return result
    }()
    
    private lazy var arrowRight: UIImageView = {
        let result = UIImageView(image: UIImage(systemName: "chevron.right")?.withRenderingMode(.alwaysTemplate))
        result.themeTintColor = .textPrimary
        result.set(.height, to: 10)
        result.set(.width, to: 5)
        return result
    }()
    
    // MARK: - Initialization
    
    init(labelInfos: [LabelInfo] = [], labelSize: CGSize = .zero, shouldAutoScroll: Bool = false) {
        super.init(frame: .zero)
        setUpViewHierarchy()
        self.update(with: labelInfos, labelSize: labelSize, shouldAutoScroll: shouldAutoScroll)
    }
    
    required init?(coder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
    
    // MARK: - Content
    
    public func update(with labelInfos: [LabelInfo], labelSize: CGSize = .zero, shouldAutoScroll: Bool = false) {
        self.labelInfos = labelInfos
        self.labelSize = labelSize
        self.shouldAutoScroll = shouldAutoScroll
        self.shouldScroll = labelInfos.count > 1
        
        if self.shouldScroll {
            let first: LabelInfo = labelInfos.first!
            let last: LabelInfo = labelInfos.last!
            self.labelInfos.append(first)
            self.labelInfos.insert(last, at: 0)
        }
        
        pageControl.numberOfPages = labelInfos.count
        pageControl.currentPage = 0
        
        let contentSize = CGSize(width: labelSize.width * CGFloat(self.labelInfos.count), height: labelSize.height)
        scrollView.contentSize = contentSize
        contentWidth.constant = contentSize.width
        contentHeight.constant = contentSize.height
        self.scrollView.setContentOffset(
            CGPoint(
                x: Int(self.labelSize.width) * (self.shouldScroll ? 1 : 0),
                y: 0
            ),
            animated: false
        )
        
        stackView.arrangedSubviews.forEach { $0.removeFromSuperview() }
        
        self.labelInfos.forEach {
            let wrapper: UIView = UIView()
            wrapper.set(.width, to: labelSize.width)
            wrapper.set(.height, to: labelSize.height)
            let label: UILabel = UILabel()
            label.font = .systemFont(ofSize: Values.miniFontSize)
            label.themeTextColor = .textPrimary
            label.lineBreakMode = .byTruncatingTail
            label.attributedText = $0.attributedText
            label.accessibilityIdentifier = $0.accessibility?.identifier
            label.accessibilityLabel = $0.accessibility?.label
            label.isAccessibilityElement = true
            wrapper.addSubview(label)
            label.center(in: wrapper)
            stackView.addArrangedSubview(wrapper)
        }
        
        if self.shouldAutoScroll {
            startScrolling()
        }
    }
    
    private func setUpViewHierarchy() {
        addSubview(scrollView)
        scrollView.pin(to: self)
        
        addSubview(arrowLeft)
        arrowLeft.pin(.left, to: .left, of: self)
        arrowLeft.center(.vertical, in: self, withInset: -4)
        
        addSubview(arrowRight)
        arrowRight.pin(.right, to: .right, of: self)
        arrowRight.center(.vertical, in: self, withInset: -4)
        
        addSubview(pageControl)
        pageControl.center(.horizontal, in: self)
        pageControl.pin(.bottom, to: .bottom, of: self)
        
        scrollView.addSubview(stackView)
    }
    
    // MARK: - Interaction
    
    private func startScrolling() {
        timer?.invalidate()
        timer = Timer.scheduledTimerOnMainThread(withTimeInterval: Self.autoScrollingTimeInterval, repeats: true) { _ in
            guard self.labelInfos.count != 0 else { return }
            let targetPage = (self.pageControl.currentPage + 1) % self.labelInfos.count
            self.scrollView.scrollRectToVisible(
                CGRect(
                    origin: CGPoint(
                        x: Int(self.labelSize.width) * targetPage,
                        y: 0
                    ),
                    size: self.labelSize
                ),
                animated: true
            )
        }
    }
    
    private func stopScrolling() {
        timer?.invalidate()
        timer = nil
    }
    
    func scrollViewDidScroll(_ scrollView: UIScrollView) {
        let pageIndex: Int = {
            let maybeCurrentPageIndex: Int = Int(round(scrollView.contentOffset.x/labelSize.width))
            if self.shouldScroll {
                if maybeCurrentPageIndex == 0 {
                    return pageControl.numberOfPages - 1
                }
                if maybeCurrentPageIndex == self.labelInfos.count - 1 {
                    return 0
                }
                return maybeCurrentPageIndex - 1
            }
            return maybeCurrentPageIndex
        }()
        
        pageControl.currentPage = pageIndex
    }
    
    func scrollViewDidEndDecelerating(_ scrollView: UIScrollView) {
        if pageControl.currentPage == 0 {
            scrollView.setContentOffset(
                CGPoint(
                    x: Int(self.labelSize.width) * 1,
                    y: 0
                ),
                animated: false
            )
        }
        
        if pageControl.currentPage == pageControl.numberOfPages - 1 {
            let realLastIndex: Int = self.labelInfos.count - 2
            scrollView.setContentOffset(
                CGPoint(
                    x: Int(self.labelSize.width) * realLastIndex,
                    y: 0
                ),
                animated: false
            )
        }
    }
}
