//
//  LockdownViewController.swift
//  Lockdown
//
//  Copyright © 2019 Confirmed Inc. All rights reserved.
//

import UIKit
import Foundation
import CocoaLumberjackSwift

final class BlockListViewController: BaseViewController {
    
    // MARK: - Properties
    var didMakeChange = false
    
    var chosenBlocking = 0
    
    var lockdownBlockLists: [LockdownGroup] = []
    var customBlockedDomains: [(String, Bool)] = []

    var customBlockedLists: [UserBlockListsGroup] = []
    
    let curatedBlockedDomainsTableView = StaticTableView()
    let customBlockedListsTableView = StaticTableView()
    let customBlockedDomainsTableView = StaticTableView()
    
    private lazy var listsSubmenuView: ListsSubmenuView = {
        let view = ListsSubmenuView()
        view.topButton.addTarget(self, action: #selector(addList), for: .touchUpInside)
        view.bottomButton.addTarget(self, action: #selector(importBlockList), for: .touchUpInside)
        view.isHidden = true
        return view
    }()
    
    private lazy var customNavigationView: CustomNavigationView = {
        let view = CustomNavigationView()
        view.title = NSLocalizedString("Configure Blocking", comment: "")
        view.buttonTitle = NSLocalizedString("CLOSE", comment: "")
        view.onButtonPressed { [unowned self] in
            self.close()
        }
        return view
    }()
    
    enum Page: CaseIterable {
        case curated
        case custom
        
        var localizedTitle: String {
            switch self {
            case .curated:
                return NSLocalizedString("Curated", comment: "")
            case .custom:
                return NSLocalizedString("Custom", comment: "")
            }
        }
    }
    
    private lazy var segmented: UISegmentedControl = {
        let view = UISegmentedControl(items: Page.allCases.map(\.localizedTitle))
        view.selectedSegmentIndex = chosenBlocking
        view.setTitleTextAttributes([.font: fontMedium14], for: .normal)
        view.selectedSegmentTintColor = .tunnelsBlue
        view.setTitleTextAttributes([.foregroundColor: UIColor.white], for: .selected)
        view.addTarget(self, action: #selector(segmentedControlDidChangeValue), for: .valueChanged)
        return view
    }()
    
    private let paragraphLabel: UILabel = {
        let view = UILabel()
        view.font = fontRegular14
        view.numberOfLines = 0
        view.text = NSLocalizedString("Block all your apps from connecting to the domains and sites below. For your convenience, Lockdown also has pre-configured suggestions.", comment: "")
        return view
    }()
    
    private lazy var addNewListButton: UIButton = {
        let button = UIButton(type: .system)
        let symbolConfig = UIImage.SymbolConfiguration(pointSize: 14, weight: .bold, scale: .large)
        button.tintColor = .tunnelsBlue
        button.setImage(UIImage(systemName: "plus", withConfiguration: symbolConfig), for: .normal)
        button.addTarget(self, action: #selector(showSubmenu), for: .touchUpInside)
        button.isEnabled = false
        return button
    }()
    
    private lazy var listsLabel: UILabel = {
        let label = UILabel()
        label.text = NSLocalizedString("Lists", comment: "")
        label.textColor = .label
        label.font = fontBold18
        return label
    }()
    
    private lazy var emptyListsView: EmptyListsView = {
        let view = EmptyListsView()
        view.descriptionLabel.text = NSLocalizedString("No lists yet", comment: "")
        view.addButton.setTitle(NSLocalizedString("Create a list", comment: ""), for: .normal)
        view.addButton.addTarget(self, action: #selector(addList), for: .touchUpInside)
        return view
    }()
    
    private lazy var lockedListsView: LockedListsView = {
        let view = LockedListsView()
        return view
    }()
    
    private lazy var domainsLabel: UILabel = {
        let label = UILabel()
        label.text = NSLocalizedString("Domains", comment: "")
        label.textColor = .label
        label.font = fontBold18
        return label
    }()
    
    private lazy var addNewDomainButton: UIButton = {
        let button = UIButton(type: .system)
        let symbolConfig = UIImage.SymbolConfiguration(pointSize: 14, weight: .bold, scale: .large)
        button.tintColor = .tunnelsBlue
        button.setImage(UIImage(systemName: "plus", withConfiguration: symbolConfig), for: .normal)
        button.addTarget(self, action: #selector(addDomain), for: .touchUpInside)
        return button
    }()
    
    private lazy var editDomainButton: UIButton = {
        let button = UIButton(type: .system)
        let symbolConfig = UIImage.SymbolConfiguration(pointSize: 14, weight: .bold, scale: .large)
        button.tintColor = .tunnelsBlue
        button.setImage(UIImage(named: "icn_edit"), for: .normal)
        button.addTarget(self, action: #selector(editDomains), for: .touchUpInside)
//        button.isHidden = true
        return button
    }()
    
    private lazy var emptyDomainsView: EmptyListsView = {
        let view = EmptyListsView()
        view.descriptionLabel.text = NSLocalizedString("No custom domains yet", comment: "")
        view.addButton.setTitle(NSLocalizedString("Add a domain", comment: ""), for: .normal)
        view.addButton.addTarget(self, action: #selector(addDomain), for: .touchUpInside)
        return view
    }()
    
    // MARK: - Lifecycle
    override func viewDidLoad() {
        super.viewDidLoad()
        view.backgroundColor = .secondarySystemBackground
        
        let tap: UITapGestureRecognizer = UITapGestureRecognizer(target: self, action: #selector(dismissView))
        tap.cancelsTouchesInView = false
        view.addGestureRecognizer(tap)
        
        configure()
        configureCuratedBlockedDomainsTableView()
        configureCustomBlockedListsTableView()
        configureCustomBlockedDomainsTableView()
    }
    
    override func viewWillAppear(_ animated: Bool) {
        super.viewWillAppear(animated)
        reloadCustomBlockedLists()
    }
    
    private func configure() {
        view.addSubview(customNavigationView)
        customNavigationView.anchors.leading.pin()
        customNavigationView.anchors.trailing.pin()
        customNavigationView.anchors.top.safeAreaPin()
        
        view.addSubview(paragraphLabel)
        paragraphLabel.anchors.top.spacing(0, to: customNavigationView.anchors.bottom)
        paragraphLabel.anchors.leading.readableContentPin(inset: 3)
        paragraphLabel.anchors.trailing.readableContentPin(inset: 3)
        paragraphLabel.anchors.height.equal(60)
        
        view.addSubview(segmented)
        segmented.anchors.top.spacing(12, to: paragraphLabel.anchors.bottom)
        segmented.anchors.leading.readableContentPin()
        segmented.anchors.trailing.readableContentPin()
        segmented.anchors.height.equal(40)
    }
    
    private func configureCuratedBlockedDomainsTableView() {
        addTableView(curatedBlockedDomainsTableView, layout: { tableView in
            tableView.anchors.top.spacing(8, to: segmented.anchors.bottom)
            tableView.anchors.leading.pin()
            tableView.anchors.trailing.pin()
            tableView.anchors.bottom.pin()
        })
        
        reloadCuratedBlockDomains()
        chosenBlocking == 0 ? transition(toPage: .curated) : transition(toPage: .custom)
    }
    
    private func configureCustomBlockedListsTableView() {
        
        view.addSubview(listsLabel)
        listsLabel.anchors.top.spacing(24, to: segmented.anchors.bottom)
        listsLabel.anchors.leading.marginsPin()

        view.addSubview(addNewListButton)
        addNewListButton.anchors.centerY.equal(listsLabel.anchors.centerY)
        addNewListButton.anchors.trailing.marginsPin()
        
        view.addSubview(domainsLabel)
        domainsLabel.anchors.top.spacing(300, to: segmented.anchors.bottom)
        domainsLabel.anchors.height.equal(30)
        domainsLabel.anchors.leading.marginsPin()
        
        addTableView(customBlockedListsTableView, layout: { tableView in
            tableView.anchors.top.spacing(8, to: listsLabel.anchors.bottom)
            tableView.anchors.leading.pin()
            tableView.anchors.trailing.pin()
            tableView.anchors.bottom.spacing(8, to: domainsLabel.anchors.top)
        })
        
        view.addSubview(listsSubmenuView)
        listsSubmenuView.anchors.trailing.marginsPin()
        listsSubmenuView.anchors.top.spacing(60, to: paragraphLabel.anchors.bottom)
        
        customBlockedListsTableView.deselectsCellsAutomatically = true
    }
    
    private func configureCustomBlockedDomainsTableView() {
        
        view.addSubview(addNewDomainButton)
        addNewDomainButton.anchors.centerY.equal(domainsLabel.anchors.centerY)
        addNewDomainButton.anchors.trailing.marginsPin()
        
//        view.addSubview(editDomainButton)
//        editDomainButton.anchors.centerY.equal(domainsLabel.anchors.centerY)
//        editDomainButton.anchors.trailing.spacing(16, to: addNewDomainButton.anchors.leading)
        
        addTableView(customBlockedDomainsTableView, layout: { tableView in
            tableView.anchors.top.spacing(8, to: domainsLabel.anchors.bottom)
            tableView.anchors.leading.pin()
            tableView.anchors.trailing.pin()
            tableView.anchors.bottom.safeAreaPin()
        })
        
        customBlockedDomainsTableView.deselectsCellsAutomatically = true

        reloadCustomBlockedDomains()
    }
    
    // Curated lists
    func reloadCuratedBlockDomains() {
        curatedBlockedDomainsTableView.clear()
        lockdownBlockLists = {
            let domains = getLockdownBlockedDomains().lockdownDefaults
            let sorted = domains.sorted(by: { $0.key < $1.key })
            return Array(sorted.map(\.value))
        }()
        createCuratedBlockedDomainsRows()
        curatedBlockedDomainsTableView.reloadData()
    }
    
    func reloadCustomBlockedLists() {
        customBlockedListsTableView.clear()
        customBlockedLists = {
            let lists = getBlockedLists().userBlockListsDefaults
            let sorted = lists.sorted(by: { $0.key < $1.key })
            return Array(sorted.map(\.value))
        }()
        createCustomBlockedListsRows()
        customBlockedListsTableView.reloadData()
    }
    
    func reloadCustomBlockedDomains() {
        customBlockedDomainsTableView.clear()
        customBlockedDomains = {
            let domains = getUserBlockedDomains()
            return domains.sorted(by: { $0.key < $1.key }).map { (key, value) -> (String, Bool) in
                if let status = value as? NSNumber {
                    return (key, status.boolValue)
                } else {
                    return (key, false)
                }
            }
        }()
        createCustomBlockedDomainsRows()
        customBlockedDomainsTableView.reloadData()
    }
    
    // Curated Lists
    func createCuratedBlockedDomainsRows() {
        let tableView = curatedBlockedDomainsTableView
        
        for lockdownGroup in lockdownBlockLists {
            
            let cell = tableView.addRow { [unowned self] (contentView) in
                let blockListView = BlockListView()
                blockListView.contents = .lockdownGroup(lockdownGroup)
                contentView.addSubview(blockListView)
                blockListView.anchors.edges.pin()
            }.onSelect { [unowned self] in
                if UserDefaults.hasSeenAdvancedPaywall || UserDefaults.hasSeenAnonymousPaywall || UserDefaults.hasSeenUniversalPaywall {
                    let storyboard = UIStoryboard.main
                    let target = storyboard.instantiate(BlockListGroupViewController.self)
                    target.lockdownGroup = lockdownGroup
//                    target.blockListVC = self
                    self.navigationController?.pushViewController(target, animated: true)
                } else {
                    if lockdownGroup.accessLevel == "advanced" {
                        let vc = VPNPaywallViewController()
                        present(vc, animated: true)
                    } else {
                        let storyboard = UIStoryboard.main
                        let target = storyboard.instantiate(BlockListGroupViewController.self)
                        target.lockdownGroup = lockdownGroup
//                        target.blockListVC = self
                        self.navigationController?.pushViewController(target, animated: true)
                    }
                }
            }
            
            cell.accessoryType = .disclosureIndicator
        }
    }
    
    @objc
    func segmentedControlDidChangeValue() {
        let page = Page.allCases[segmented.selectedSegmentIndex]
        transition(toPage: page)
    }
    
    func transition(toPage page: Page) {
        
        switch page {
        case .curated:
            customBlockedDomainsTableView.isHidden = true
            customBlockedListsTableView.isHidden = true
            curatedBlockedDomainsTableView.isHidden = false
            listsLabel.isHidden = true
            addNewListButton.isHidden = true
            listsSubmenuView.isHidden = true
            addNewDomainButton.isHidden = true
            domainsLabel.isHidden = true
            editDomainButton.isHidden = true
        case .custom:
            customBlockedListsTableView.isHidden = false
            customBlockedDomainsTableView.isHidden = false
            curatedBlockedDomainsTableView.isHidden = true
            listsLabel.isHidden = false
            addNewListButton.isHidden = false
            addNewDomainButton.isHidden = false
            domainsLabel.isHidden = false
            editDomainButton.isHidden = false
        }
    }
    
    func saveNewList(userEnteredListName: String) {
        didMakeChange = true
        DDLogInfo("Adding custom list - \(userEnteredListName)")
        addBlockedList(listName: userEnteredListName)
        reloadCustomBlockedLists()
    }
    
    func saveNewDomain(userEnteredDomainName: String) {
        let validation = DomainNameValidator.validate(userEnteredDomainName)
        
        switch validation {
        case .valid:
            didMakeChange = true
            
            DDLogInfo("Adding custom domain - \(userEnteredDomainName)")
            addUserBlockedDomain(domain: userEnteredDomainName.lowercased())
            reloadCustomBlockedDomains()
        case .notValid(let reason):
            DDLogWarn("Custom domain is not valid - \(userEnteredDomainName), reason - \(reason)")
            showPopupDialog(
                title: NSLocalizedString("Invalid domain", comment: ""),
                message: "\"\(userEnteredDomainName)\"" + NSLocalizedString(" is not a valid entry. Please only enter the host of the domain you want to block. For example, \"google.com\" without \"https://\"", comment: ""),
                acceptButton: NSLocalizedString("Okay", comment: "")
            )
        }
    }
}

// MARK: - Functions
extension BlockListViewController {
    
    func createCustomBlockedListsRows() {
        let tableView = customBlockedListsTableView
        let emptyList = emptyListsView
        let lockedList = lockedListsView
        
        if UserDefaults.hasSeenAdvancedPaywall || UserDefaults.hasSeenAnonymousPaywall || UserDefaults.hasSeenUniversalPaywall {
            addNewListButton.isEnabled = true
            if customBlockedLists.count == 0 {
                tableView.addRow { [unowned self] (contentView) in
                    contentView.addSubview(emptyList)
                    emptyListsView.anchors.edges.pin()
                }.onSelect { [unowned self] in
                    self.addList()
                }
            }
        } else {
            tableView.addRow { [unowned self] (contentView) in
                contentView.addSubview(lockedList)
                lockedList.anchors.edges.pin()
            }.onSelect { [unowned self] in
                let vc = VPNPaywallViewController()
                self.present(vc, animated: true)
            }
        }
        
        for list in customBlockedLists {
            let blockListView = BlockListView()
            blockListView.contents = .listsBlocked(list)
            
            let cell = tableView.addRow { [unowned self] (contentView) in
                contentView.addSubview(blockListView)
                blockListView.anchors.edges.pin()
            }.onSelect { [unowned self] in
                self.didMakeChange = true
                let vc = ListSettingsViewController()
                vc.listName = list.name
                vc.didMakeChange = list.enabled
                vc.blockListVC = self
                navigationController?.pushViewController(vc, animated: true)
            }.onSwipeToDelete { [unowned self] in
                self.didMakeChange = true
                deleteList(list: list.name)
                DDLogInfo("Deleting custom list - \(list.name)")
            }
            cell.accessoryType = .disclosureIndicator
        }
    }
    
    // Custom Domains
    func createCustomBlockedDomainsRows() {
        let tableView = customBlockedDomainsTableView
        let emptyDomains = emptyDomainsView
        if customBlockedDomains.count == 0 {
            tableView.addRow { [unowned self] (contentView) in
                contentView.addSubview(emptyDomains)
                emptyDomains.anchors.edges.pin()
            }.onSelect { [unowned self] in
                self.addDomain()
            }
        }

        for (domain, isEnabled) in customBlockedDomains {
            var currentEnabledStatus = isEnabled
            let blockListView = BlockListView()
            blockListView.contents = .userBlocked(domain: domain, isEnabled: isEnabled)
            
            tableView.addRow { [unowned self] (contentView) in
                contentView.addSubview(blockListView)
                blockListView.anchors.edges.pin()
                view.addSubview(editDomainButton)
                editDomainButton.anchors.centerY.equal(domainsLabel.anchors.centerY)
                editDomainButton.anchors.trailing.spacing(16, to: addNewDomainButton.anchors.leading)
            }.onSelect { [unowned blockListView, unowned self] in
                self.didMakeChange = true
                currentEnabledStatus.toggle()
                blockListView.contents = .userBlocked(domain: domain, isEnabled: currentEnabledStatus)
                setUserBlockedDomain(domain: domain, enabled: currentEnabledStatus)
            }.onSwipeToDelete { [unowned self] in
                self.didMakeChange = true
                deleteUserBlockedDomain(domain: domain)
                DDLogInfo("Deleting custom domain - \(domain)")
            }
        }
    }
    
    func close() {
        dismiss(animated: true, completion: { [weak self] in
            guard let self else { return }
            if (self.didMakeChange == true) {
                if getIsCombinedBlockListEmpty() {
                    FirewallController.shared.setEnabled(false, isUserExplicitToggle: true)
                } else if (FirewallController.shared.status() == .connected) {
                    FirewallController.shared.restart()
                }
            }
        })
    }
    
    @objc func addList() {
        
        let tableView = customBlockedListsTableView

        let alertController = UIAlertController(title: "Create New List", message: nil, preferredStyle: .alert)
        
        let saveAction = UIAlertAction(title: "Save", style: .default) { [weak self] (_) in
            if let txtField = alertController.textFields?.first, let text = txtField.text {
                guard let self else { return }
                self.saveNewList(userEnteredListName: text)
//                if !getBlockedLists().isEmpty {
//                    tableView.clear()
//                }
                self.reloadCustomBlockedLists()
                self.listsSubmenuView.isHidden = true
            }
        }
        
        saveAction.isEnabled = false
        
        let cancelAction = UIAlertAction(title: "Cancel", style: .cancel) { [weak self] (_) in
            guard let self else { return }
            self.listsSubmenuView.isHidden = true
        }
        
        alertController.addTextField { (textField) in
            textField.placeholder = NSLocalizedString("List Name", comment: "")
        }
        
        NotificationCenter.default.addObserver(
            forName: UITextField.textDidChangeNotification,
            object: alertController.textFields?.first,
            queue: .main) { (notification) -> Void in
                guard let textFieldText = alertController.textFields?.first?.text else { return }
                saveAction.isEnabled = textFieldText.isValid(.listName)
            }
        
        alertController.addAction(saveAction)
        alertController.addAction(cancelAction)
        present(alertController, animated: true, completion: nil)
    }
    
    func deleteList(list: String) {
        
        let alert = UIAlertController(title: NSLocalizedString("Delete List?", comment: ""),
                                      message: NSLocalizedString("Are you sure you want to remove this list?", comment: ""),
                                      preferredStyle: .alert)
        
        alert.addAction(UIAlertAction(title: NSLocalizedString("No, Return", comment: ""),
                                      style: .default,
                                      handler: { [weak self] (_) in
            guard let self else { return }
            self.reloadCustomBlockedLists()
        }))
        
        alert.addAction(UIAlertAction(title: NSLocalizedString("Yes, Delete", comment: ""),
                                      style: .destructive,
                                      handler: { [weak self] (_) in
            guard let self else { return }
            deleteBlockedList(listName: list)
            self.customBlockedListsTableView.clear()
            self.reloadCustomBlockedLists()
        }))
        
        present(alert, animated: true, completion: nil)
    }
    
    @objc func showSubmenu() {
        listsSubmenuView.isHidden = false
    }
    
    @objc func dismissView() {
        listsSubmenuView.isHidden = true
    }
    
    @objc func importBlockList() {
        listsSubmenuView.isHidden = true
        let vc = ImportBlockListViewController()
        vc.importCompletion = { [unowned self] in
                self.reloadCustomBlockedLists()
                self.showSuccessImportAlert()
        }
        
        navigationController?.present(vc, animated: true)
    }
    
    @objc func addDomain() {
        let tableView = customBlockedDomainsTableView
        
        let alertController = UIAlertController(title: NSLocalizedString("Add a Domain to Block", comment: ""),
                                                message: nil,
                                                preferredStyle: .alert)
        
        let saveAction = UIAlertAction(title: "Save", style: .default) { [weak self] (_) in
            if let txtField = alertController.textFields?.first, let text = txtField.text {
                guard let self else { return }
                self.saveNewDomain(userEnteredDomainName: text)
                if !getUserBlockedDomains().isEmpty {
                    tableView.clear()
                }
                
                self.reloadCustomBlockedDomains()
//                self.editDomainButton.isHidden = false
            }
        }
        
        saveAction.isEnabled = false
        
        let cancelAction = UIAlertAction(title: "Cancel", style: .cancel) { (_) in }
        
        alertController.addTextField { (textField) in
            textField.keyboardType = .URL
            textField.placeholder = "domain-to-block"
        }
        
        NotificationCenter.default.addObserver(
            forName: UITextField.textDidChangeNotification,
            object: alertController.textFields?.first,
            queue: .main) { (notification) -> Void in
                guard let textFieldText = alertController.textFields?.first?.text else { return }
                saveAction.isEnabled = textFieldText.isValid(.domainName) && !textFieldText.isEmpty
            }
        
        alertController.addAction(saveAction)
        alertController.addAction(cancelAction)
        present(alertController, animated: true, completion: nil)
    }
    
    @objc func editDomains() {
        if !customBlockedDomains.isEmpty {
            let vc = EditDomainsViewController()
            vc.updateCompletion = { [weak self] in
                self?.reloadCustomBlockedDomains()
            }
            navigationController?.pushViewController(vc, animated: true)
        }
    }
    
    func showSuccessImportAlert() {
        let alert = UIAlertController(title: NSLocalizedString("Success!", comment: ""),
                                      message: NSLocalizedString("The list has been imported successfully. You can start blocking the list's domains", comment: ""),
                                      preferredStyle: .alert)
        alert.addAction(UIAlertAction(title: NSLocalizedString("Close", comment: ""),
                                      style: .default,
                                      handler: nil))
        present(alert, animated: true, completion: nil)
    }
    
    func showErrorAlert() {
        let alert = UIAlertController(title: NSLocalizedString("Error", comment: ""),
                                      message: NSLocalizedString("Unable to import the list. Please try again or contact support for assistance", comment: ""),
                                      preferredStyle: .alert)
        alert.addAction(UIAlertAction(title: NSLocalizedString("Close", comment: ""),
                                      style: .default,
                                      handler: nil))
        present(alert, animated: true, completion: nil)
    }
}
