//
//  CustomTableView.swift
//
//  Created by Aliaksandr Dvoineu on 16.03.23.
//

import UIKit

class TableViewHeader: UIView {
    
    lazy var view: UIView = {
        let view = UIView()
        return view
    }()
    
    override init(frame: CGRect) {
        super.init(frame: .zero)
        configure()
    }
    
    required init?(coder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
    
    func configure() {
        addSubview(view)
        view.anchors.edges.pin()
    }
}

class CustomTableViewCell: UITableViewCell {
    var selectionCallback: () -> () = { }
    var deletionCallback: (() -> ())?
    
    enum Action {
        case toggleCheckmark
    }
    
    @discardableResult
    func onSelect(callback: @escaping () -> ()) -> Self {
        selectionStyle = .default
        selectionCallback = callback
        return self
    }
    
    @discardableResult
    func onSwipeToDelete(callback: @escaping () -> ()) -> Self {
        deletionCallback = callback
        return self
    }
    
    @discardableResult
    func onSelect(_ action: Action, callback: @escaping () -> () = { }) -> Self {
        selectionCallback = { [unowned self] in
            switch action {
            case .toggleCheckmark:
                if self.accessoryType == .checkmark {
                    self.accessoryType = .none
                } else {
                    self.accessoryType = .checkmark
                }
            }
            callback()
        }
        return self
    }
}

final class CustomTableView: UITableView {
    
    // Resizing UITableView to fit content
    override var contentSize: CGSize {
        didSet {
            invalidateIntrinsicContentSize()
        }
    }
    
    override var intrinsicContentSize: CGSize {
        layoutIfNeeded()
        return CGSize(width: UIView.noIntrinsicMetric, height: contentSize.height)
    }
    
    var rows: [CustomTableViewCell] = []
    var deselectsCellsAutomatically: Bool = false
    var headerView = TableViewHeader()
    
    override init(frame: CGRect, style: UITableView.Style) {
        super.init(frame: frame, style: .grouped)
        setup()
    }
    
    required init?(coder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
    
    func setup() {
        dataSource = self
        delegate = self
        separatorStyle = .none
    }
    
    enum Insert {
        case last
        case dontInsert
    }
    
    @discardableResult
    func addHeader(_ configure: (UIView) -> ()) -> TableViewHeader {
        let header = headerView
        configure(header.view)
        return header
    }
    
    @discardableResult
    func addRow(insert: Insert = .last, _ configure: (UIView) -> ()) -> CustomTableViewCell {
        let cell = CustomTableViewCell()
        cell.selectionStyle = .none
        cell.backgroundColor = nil
        configure(cell.contentView)
        self.insert(cell: cell, insert: insert)
        return cell
    }
    
    @discardableResult
    func addRowCell(insert: Insert = .last, _ configure: (UITableViewCell) -> ()) -> CustomTableViewCell {
        let cell = CustomTableViewCell()
        cell.selectionStyle = .none
        configure(cell)
        self.insert(cell: cell, insert: insert)
        return cell
    }
    
    @discardableResult
    func addCell(insert: Insert = .last, _ cell: CustomTableViewCell) -> CustomTableViewCell {
        self.insert(cell: cell, insert: insert)
        return cell
    }
    
    @discardableResult
    func addRow(insert: Insert = .last, view: UIView, insets: UIEdgeInsets = .zero) -> CustomTableViewCell {
        return addRow(insert: insert) { (row) in
            row.addSubview(view)
            view.anchors.edges.pin(axis: .vertical)
            view.anchors.edges.marginsPin(insets: insets, axis: .horizontal)
        }
    }
    
    private func insert(cell: CustomTableViewCell, insert: Insert) {
        switch insert {
        case .dontInsert:
            break
        case .last:
            rows.append(cell)
        }
    }
    
    func clear() {
        rows = []
    }
    
}

extension CustomTableView: UITableViewDataSource {
    
    func numberOfSections(in tableView: UITableView) -> Int {
        return 1
    }
    
    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return rows.count
    }
    
    func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
        let header = headerView
        return header
    }
    
    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        return rows[indexPath.row]
    }
    
    func tableView(_ tableView: UITableView, canEditRowAt indexPath: IndexPath) -> Bool {
        let cell = rows[indexPath.row]
        return cell.deletionCallback != nil
    }
    
    func tableView(_ tableView: UITableView, editingStyleForRowAt indexPath: IndexPath) -> UITableViewCell.EditingStyle {
        return .delete
    }
    
    func tableView(_ tableView: UITableView, commit editingStyle: UITableViewCell.EditingStyle, forRowAt indexPath: IndexPath) {
        let cell = rows[indexPath.row]
        guard cell.deletionCallback != nil else {
            return
        }
        cell.deletionCallback?()
        self.rows.removeAll(where: { $0 === cell })
        self.deleteRows(at: [indexPath], with: .fade)
    }
}

extension CustomTableView: UITableViewDelegate {
    func tableView(_ tableView: UITableView, shouldHighlightRowAt indexPath: IndexPath) -> Bool {
        if let cell = tableView.cellForRow(at: indexPath) as? CustomTableViewCell {
            return cell.selectionStyle != .none
        } else {
            return false
        }
    }
    
    func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
        if let cell = tableView.cellForRow(at: indexPath) as? CustomTableViewCell {
            cell.selectionCallback()
            if deselectsCellsAutomatically {
                tableView.deselectRow(at: indexPath, animated: true)
            }
        }
    }
}
