//
//  ScriptPolicyViewController.swift
//  SBrowser
//
//  Created by James Magahern on 7/24/20.
//

import UIKit

extension ScriptPolicy.PolicyType : CaseIterable {
    public static var allCases: [ScriptPolicy.PolicyType] = [
        .alpha,
        .bravo,
        .charlie,
        .delta,
        .echo
    ]
}

class ScriptPolicyViewController: UIViewController, UICollectionViewDelegate
{
    var collectionView: UICollectionView?
    var allowScriptsForTab = false
    weak var delegate: ScriptPolicyViewControllerDelegate? = nil
    
    private enum Section: Int {
        case tabOptions
        case policies
    }

    private enum Item: Hashable {
        case allowScriptsForTab
        case policy(ScriptPolicy.PolicyType)
    }
    
    private var dataSource: UICollectionViewDiffableDataSource<Section, Item>?
    private var didChangeScriptPolicy = false
    private var policyManager: ResourcePolicyManager!
    private var hostOrigin: String!
    
    convenience init(policyManager: ResourcePolicyManager, hostOrigin: String, loadedScripts: Set<String>, scriptsAllowedForTab: Bool) {
        self.init(nibName: nil, bundle: nil)
        self.allowScriptsForTab = scriptsAllowedForTab
        self.policyManager = policyManager
        self.hostOrigin = hostOrigin
        
        let listConfig = UICollectionLayoutListConfiguration(appearance: .insetGrouped)
        let listLayout = UICollectionViewCompositionalLayout.list(using: listConfig)
        let collectionView = UICollectionView(frame: .zero, collectionViewLayout: listLayout)
        
        let scriptPolicyRegistry = UICollectionView.CellRegistration<UICollectionViewListCell, Item> { (listCell, indexPath, item) in
            guard case let Item.policy(policyType) = item else { return }
            
            let iconSize = CGSize(width: 24.0, height: 24.0)
            
            var config = listCell.defaultContentConfiguration()
            config.text = ScriptPolicy.title(forPolicyType: policyType)
            config.secondaryText = ScriptPolicy.localizedDescription(forPolicyType: policyType)
            config.image = ScriptPolicy.iconRepresentation(forPolicyType: policyType, configuration: ScriptPolicy.IconConfiguration(
                size: iconSize,
                foregroundColor: .white,
                backgroundColor: .black
            ))
            
            config.textProperties.font = UIFont.boldSystemFont(ofSize: 14.0)
            
            if policyManager.scriptPolicy(forOrigin: hostOrigin).policyType == policyType {
                listCell.accessories = [ .checkmark() ]
            }
            
            listCell.contentConfiguration = config
        }
        
        let enableButtonRegistry = UICollectionView.CellRegistration<UICollectionViewListCell, Item> { [unowned self] (listCell, indexPath, item) in
            var config = listCell.defaultContentConfiguration()
            if indexPath.section == Section.tabOptions.rawValue {
                if allowScriptsForTab {
                    config.text = "Shields Up"
                    config.image = UIImage(systemName: "shield.fill")
                } else {
                    config.text = "Allow for Tab"
                    config.image = UIImage(systemName: "shield.slash")
                }
                
                config.textProperties.color = UIColor.systemBlue
            }
            
            listCell.contentConfiguration = config
        }
        
        let dataSource = UICollectionViewDiffableDataSource<Section, Item>(collectionView: collectionView) { (collectionView, indexPath, item) -> UICollectionViewCell? in
            if indexPath.section == Section.tabOptions.rawValue {
                return collectionView.dequeueConfiguredReusableCell(using: enableButtonRegistry, for: indexPath, item: item)
            }
            
            return collectionView.dequeueConfiguredReusableCell(using: scriptPolicyRegistry, for: indexPath, item: item)
        }
        
        collectionView.dataSource = dataSource
        collectionView.delegate = self
        
        var snapshot = dataSource.snapshot()
        snapshot.appendSections([ .tabOptions ])
        snapshot.appendItems([ .allowScriptsForTab ], toSection: .tabOptions)
        
        if !allowScriptsForTab {
            snapshot.appendSections([ .policies ])
            snapshot.appendItems(ScriptPolicy.PolicyType.allCases.map { .policy($0) }, toSection: .policies)
        }
        
        dataSource.apply(snapshot)
        
        self.dataSource = dataSource
        self.collectionView = collectionView
        
        title = hostOrigin
        navigationItem.rightBarButtonItem = UIBarButtonItem(systemItem: .done, primaryAction: UIAction(handler: { [unowned self] action in
            if self.didChangeScriptPolicy {
                self.delegate?.didChangeScriptPolicy()
                self.delegate?.setScriptsEnabledForTab(self.allowScriptsForTab)
            }
            
            self.dismiss(animated: true, completion: nil)
        }), menu: nil)
    }
    
    override func loadView() {
        self.view = collectionView
        self.view.backgroundColor = .systemGroupedBackground
    }
    
    // MARK: UICollectionViewDelegate
    
    func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
        guard let dataSource = dataSource else { return }
        
        if indexPath.section == Section.tabOptions.rawValue {
            let identifier = dataSource.itemIdentifier(for: indexPath)
            if identifier == .allowScriptsForTab {
                self.allowScriptsForTab = !self.allowScriptsForTab
                
                // Immediately notify and dismiss
                self.delegate?.didChangeScriptPolicy()
                self.delegate?.setScriptsEnabledForTab(self.allowScriptsForTab)
                self.dismiss(animated: true, completion: nil)
            }
        } else if indexPath.section == Section.policies.rawValue {
            guard let identifier = dataSource.itemIdentifier(for: indexPath) else { return }
            guard case let Item.policy(policyType) = identifier else { return }
            
            var snapshot = dataSource.snapshot()
            snapshot.reloadItems([ identifier ])
            let selectedItem = Item.policy(policyManager.scriptPolicy(forOrigin: hostOrigin).policyType)
            snapshot.reloadItems([ selectedItem ])
            
            policyManager.setScriptPolicyType(policyType, forOrigin: hostOrigin)
            dataSource.apply(snapshot)
            
            delegate?.didChangeScriptPolicy()
            collectionView.deselectItem(at: indexPath, animated: true)
            dismiss(animated: true, completion: nil)
        }
    }
}
