//
//  CollectionViewComponent.swift
//  ClockWidget
//
//  Created by TSYH on 2023/7/18.
//

import UIKit

public class CollectionView: UICollectionView {
    lazy var reuseSubviews: [String: UIView] = [:]
    
    func cacheReuseView(_ view: UIView, reuseId: String?) {
        guard let reuseId = reuseId, !reuseId.isEmpty else {
            return
        }
        reuseSubviews[reuseId] = view
    }
    
    func dequeueReuseSubviews(for reuseId: String?) -> UIView? {
        guard let reuseId = reuseId, !reuseId.isEmpty else {
            return nil
        }
        return reuseSubviews[reuseId]
    }
    
    func clearReuseSubviews() {
        reuseSubviews.removeAll()
    }
}

public class CollectionViewComponent: NSObject {
    var itemDidSelectedHandler: ((Any?, IndexPath) -> Void)?
    var itemActionHandler: ((Any?, IndexPath) -> Void)?
    
    var itemWillDisplayHandler: ((UICollectionViewCell, Any?, IndexPath) -> Void)?
    var itemDidEndDisplayingHandler: ((UICollectionViewCell, Any?, IndexPath) -> Void)?
    
    var didScrollHandler: ((UIScrollView) -> Void)?
    var willBeginDraggingHandler: ((UIScrollView) -> Void)?
    var didEndDraggingHandler: ((UIScrollView, Bool) -> Void)?
    var didScrollToTopHandler: ((UIScrollView) -> Void)?
    var willEndDraggingWithVelocityHandler: ((UIScrollView, CGPoint) -> Void)?
    var didEndDeceleratingHandler: ((UIScrollView) -> Void)?
    
    var collectionView: CollectionView!
    var attributes: [String: Any]?
    
    
    
    
    
    static let defaultCellID: String = "Component_Default_Cell_ID"
    init(frame: CGRect, layout: UICollectionViewFlowLayout? = nil, attributes: [String: Any]? = nil) {
        super.init()
        
        var clLayout = UICollectionViewFlowLayout()
        if let direction = attributes?["direction"] as? UICollectionView.ScrollDirection {
            clLayout.scrollDirection = direction
        }
        
        if let layout = layout {
            clLayout = layout
        }
        collectionView = CollectionView(frame: frame, collectionViewLayout: clLayout)
        collectionView.delegate = self
        collectionView.dataSource = self
        collectionView.contentInsetAdjustmentBehavior = .never
        collectionView.showsVerticalScrollIndicator = false
        collectionView.showsHorizontalScrollIndicator = false
        collectionView.backgroundColor = .clear
        
        self.attributes = attributes
        if let showScrollIndicator = attributes?["showScrollIndicator"] as? Bool {
            collectionView.showsHorizontalScrollIndicator = showScrollIndicator
        }
        
        collectionView.register(UICollectionViewCell.self, forCellWithReuseIdentifier: Self.defaultCellID)
    }
    
    lazy var dataSource = [Component]()
    func clear() {
        dataSource = []
    }
    
    func reloadView(with components: [Component]) {
        registerClass(components: components)
        dataSource.append(contentsOf: components)
        collectionView.clearReuseSubviews()
        collectionView.reloadData()
    }
    
    func reloadData() {
        collectionView.clearReuseSubviews()
        collectionView.reloadData()
    }
    
    // 注册cell
    private func registerClass(components: [Component]) {
        for cp in components {
            if let sectionCp = cp as? CollectionViewSectionComponent {
                if let header = sectionCp.headerComponent {
                    collectionView.register(header.viewClass, forSupplementaryViewOfKind: UICollectionView.elementKindSectionHeader, withReuseIdentifier: header.reuseIdentifier)
                }
                if let footer = sectionCp.footerComponent {
                    collectionView.register(footer.viewClass, forSupplementaryViewOfKind: UICollectionView.elementKindSectionFooter, withReuseIdentifier: footer.reuseIdentifier)
                }
                for cellCp in sectionCp.cells {
                    collectionView.register(cellCp.cellClass, forCellWithReuseIdentifier: cellCp.cellClass.description())
                }
            } else if let cellCp = cp as? CollectionViewCellComponent {
                collectionView.register(cellCp.cellClass, forCellWithReuseIdentifier: cellCp.cellClass.description())
            }
        }
    }
}

extension CollectionViewComponent {
    private var isSectionFramework: Bool {
        if dataSource.first is CollectionViewSectionComponent {
            return true
        }
        return false
    }
    
    func cellComponent(at indexPath: IndexPath) -> CollectionViewCellComponent? {
        var cellCp: Component?
        if isSectionFramework {
            if indexPath.section < dataSource.count,
                let sectionCp = dataSource[indexPath.section] as? CollectionViewSectionComponent,
                indexPath.item < sectionCp.cells.count {
                cellCp = sectionCp.cells[indexPath.item]
            }
        } else if indexPath.item < dataSource.count {
            cellCp = dataSource[indexPath.item]
        }
        if let cellCp = cellCp as? CollectionViewCellComponent {
            return cellCp
        }
        return nil
    }
}

extension CollectionViewComponent: UICollectionViewDataSource {
    
    public func numberOfSections(in collectionView: UICollectionView) -> Int {
        if isSectionFramework {
            return dataSource.count
        }
        return 1
    }
    
    public func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
        if isSectionFramework {
            if section < dataSource.count {
                return (dataSource[section] as? CollectionViewSectionComponent)?.cells.count ?? 0
            }
            return 0
        }
        return dataSource.count
    }
    
    public func collectionView(_ collectionView: UICollectionView, willDisplay cell: UICollectionViewCell, forItemAt indexPath: IndexPath) {
        itemWillDisplayHandler?(cell, cellComponent(at: indexPath), indexPath)
    }
    
    public func collectionView(_ collectionView: UICollectionView, didEndDisplaying cell: UICollectionViewCell, forItemAt indexPath: IndexPath) {
        itemDidEndDisplayingHandler?(cell, cellComponent(at: indexPath), indexPath)
    }
    
    public func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
        guard let cellCp = cellComponent(at: indexPath) else {
            return collectionView.dequeueReusableCell(withReuseIdentifier: Self.defaultCellID, for: indexPath)
        }
        
        let cellClass = cellCp.cellClass
        let cell = collectionView.dequeueReusableCell(withReuseIdentifier: cellClass.description(), for: indexPath)
        if let cell = cell as? ComponentView {
            var atts = attributes ?? [:]
            atts["indexPath"] = indexPath
            cell.renderView(with: cellCp, component: self, attributes: atts)
        }
        return cell
    }
    
    public func collectionView(_ collectionView: UICollectionView, viewForSupplementaryElementOfKind kind: String, at indexPath: IndexPath) -> UICollectionReusableView {
        guard let sectionCp = dataSource[indexPath.section] as? CollectionViewSectionComponent else {
            return UICollectionReusableView()
        }
        if kind == UICollectionView.elementKindSectionHeader {
            if let headerCp = sectionCp.headerComponent {
                let header = collectionView.dequeueReusableSupplementaryView(ofKind: kind, withReuseIdentifier: headerCp.reuseIdentifier, for: indexPath)
                if let headerView = header as? ComponentView {
                    headerView.renderView(with: headerCp, component: self, attributes: attributes)
                }
                return header
            }
        } else if kind == UICollectionView.elementKindSectionFooter {
            if let footerCp = sectionCp.headerComponent {
                let footer = collectionView.dequeueReusableSupplementaryView(ofKind: kind, withReuseIdentifier: footerCp.reuseIdentifier, for: indexPath)
                if let footerView = footer as? ComponentView {
                    footerView.renderView(with: footerCp, component: self, attributes: attributes)
                }
                return footer
            }
        }
        return UICollectionReusableView()
    }
}

extension CollectionViewComponent: UICollectionViewDelegateFlowLayout {
    public func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
        guard let cellCp = cellComponent(at: indexPath) else {
            return .zero
        }
        return cellCp.cellSize(with: attributes)
    }
    
    public func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, insetForSectionAt section: Int) -> UIEdgeInsets {
        if isSectionFramework {
            if section < dataSource.count {
                return (dataSource[section] as? CollectionViewSectionComponent)?.sectionInset ?? .zero
            }
        }
        return UIEdgeInsets(top: 10, left: 16, bottom: 10, right: 16)
    }
    
    public func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumLineSpacingForSectionAt section: Int) -> CGFloat {
        if section < dataSource.count {
            return (dataSource[section] as? CollectionViewSectionComponent)?.lineSpacing ?? 12.0
        }
        return 12.0
    }
    
    public func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumInteritemSpacingForSectionAt section: Int) -> CGFloat {
        if section < dataSource.count {
            return (dataSource[section] as? CollectionViewSectionComponent)?.itemSpacing ?? 12.0
        }
        return 12.0
    }
    
    public func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, referenceSizeForFooterInSection section: Int) -> CGSize {
        if isSectionFramework {
            if section < dataSource.count,
                let sectionCp = dataSource[section] as? CollectionViewSectionComponent {
                return sectionCp.footerComponent?.viewSize ?? .zero
            }
        }
        return .zero
    }
    
    public func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, referenceSizeForHeaderInSection section: Int) -> CGSize {
        if isSectionFramework {
            if section < dataSource.count,
                let sectionCp = dataSource[section] as? CollectionViewSectionComponent {
                return sectionCp.headerComponent?.viewSize ?? .zero
            }
        }
        return .zero
    }
}

extension CollectionViewComponent: UICollectionViewDelegate {
    public func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
        itemDidSelectedHandler?(cellComponent(at: indexPath), indexPath)
    }
    
    public func scrollViewDidScroll(_ scrollView: UIScrollView) {
        didScrollHandler?(scrollView)
    }
    
    public func scrollViewWillEndDragging(_ scrollView: UIScrollView, withVelocity velocity: CGPoint, targetContentOffset: UnsafeMutablePointer<CGPoint>) {
        willEndDraggingWithVelocityHandler?(scrollView, velocity)
    }
    
    public func scrollViewWillBeginDragging(_ scrollView: UIScrollView) {
        willBeginDraggingHandler?(scrollView)
    }
    
    public func scrollViewDidEndDragging(_ scrollView: UIScrollView, willDecelerate decelerate: Bool) {
        didEndDraggingHandler?(scrollView, decelerate)
    }
    
    public func scrollViewDidScrollToTop(_ scrollView: UIScrollView) {
        didScrollToTopHandler?(scrollView)
    }
    
    public func scrollViewDidEndDecelerating(_ scrollView: UIScrollView) {
        didEndDeceleratingHandler?(scrollView)
    }
}
