// 
// Copyright 2022 New Vector Ltd
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//

import UIKit
import MatrixSDK

enum AllChatsEditActionProviderOption {
    case exploreRooms
    case createRoom
    case startChat
    case createSpace
}

protocol AllChatsEditActionProviderDelegate: AnyObject {
    func allChatsEditActionProvider(_ actionProvider: AllChatsEditActionProvider, didSelect option: AllChatsEditActionProviderOption)
}

/// `AllChatsEditActionProvider` provides the menu for accessing edit screens according to the current parent space
class AllChatsEditActionProvider {
    
    // MARK: - Properties
    
    weak var delegate: AllChatsEditActionProviderDelegate?
    
    // MARK: - Private
    
    private var rootSpaceCount: Int = 0
    private var parentSpace: MXSpace? {
        didSet {
            parentName = parentSpace?.summary?.displayName ?? VectorL10n.spaceTag
        }
    }
    private var parentName: String = VectorL10n.spaceTag
    private var isInviteAvailable: Bool = false
    private var isAddRoomAvailable: Bool = true

    // MARK: - RoomActionProviderProtocol
    
    var menu: UIMenu {
        guard parentSpace != nil else {
            var createActions = [
                self.createRoomAction,
                self.startChatAction
            ]
            if rootSpaceCount > 0 {
                createActions.insert(self.createSpaceAction, at: 0)
            }
            return UIMenu(title: "", children: [
                self.exploreRoomsAction,
                UIMenu(title: "", options: .displayInline, children: createActions)
            ])
        }
        
        return UIMenu(title: "", children: [
            UIMenu(title: "", options: .displayInline, children: [
                self.exploreRoomsAction
            ]),
            UIMenu(title: "", options: .displayInline, children: [
                self.createSpaceAction,
                self.createRoomAction
            ])
        ])
    }
    
    // MARK: - Public
    
    /// Indicates if the context menu should be updated accordingly to the given parameters.
    ///
    /// If `shouldUpdate()` reutrns `true`, you should update the context menu by calling `updateMenu()`.
    ///
    /// - Parameters:
    ///     - session: The current `MXSession` instance
    ///     - parentSpace: The current parent space (`nil` for home space)
    /// - Returns: `true` if the context menu should be updated (call `updateMenu()` in this case). `false` otherwise
    func shouldUpdate(with session: MXSession?, parentSpace: MXSpace?) -> Bool {
        let rootSpaceCount = session?.spaceService.rootSpaces.count ?? 0
        return parentSpace != self.parentSpace || (rootSpaceCount == 0 && self.rootSpaceCount > 0 || rootSpaceCount > 0 && self.rootSpaceCount == 0)
    }
    
    /// Returns an instance of the updated menu accordingly to the given parameters.
    ///
    /// Some menu items can be disabled depending on the required power levels of the `parentSpace`. Therefore, `updateMenu()` first returns a temporary context menu
    /// with all sensible items disabled, asynchronously fetches power levels of the `parentSpace`, then gives a new instance of the menu with, potentially, all sensible items
    /// enabled via the `completion` callback.
    ///
    /// - Parameters:
    ///     - session: The current `MXSession` instance
    ///     - parentSpace: The current parent space (`nil` for home space)
    ///     - completion: callback called once the power levels of the `parentSpace` have been fetched and the menu items have been computed accordingly.
    /// - Returns: If the `parentSpace` is `nil`, the context menu, the temporary context menu otherwise.
    func updateMenu(with session: MXSession?, parentSpace: MXSpace?, completion: @escaping (UIMenu) -> Void) -> UIMenu {
        self.parentSpace = parentSpace
        self.rootSpaceCount = session?.spaceService.rootSpaces.count ?? 0
        isInviteAvailable = false
        isAddRoomAvailable = parentSpace == nil
        
        guard let parentSpace = parentSpace, let spaceRoom = parentSpace.room, let session = session else {
            return self.menu
        }
        
        spaceRoom.state { [weak self] roomState in
            guard let self = self else { return }
            
            guard let powerLevels = roomState?.powerLevels, let userId = session.myUserId else {
                return
            }
            let userPowerLevel = powerLevels.powerLevelOfUser(withUserID: userId)

            self.isInviteAvailable = userPowerLevel >= powerLevels.invite
            self.isAddRoomAvailable = userPowerLevel >= parentSpace.minimumPowerLevelForAddingRoom(with: powerLevels)
            
            completion(self.menu)
        }
        
        return self.menu
    }
    
    // MARK: - Private
    
    private var exploreRoomsAction: UIAction {
        UIAction(title: parentSpace == nil ? VectorL10n.spacesExploreRooms : VectorL10n.spacesExploreRoomsFormat(parentName),
                 image: UIImage(systemName: "list.bullet")) { [weak self] action in
            guard let self = self else { return }
            
            self.delegate?.allChatsEditActionProvider(self, didSelect: .exploreRooms)
        }
    }
    
    private var createRoomAction: UIAction {
        UIAction(title: parentSpace == nil ? VectorL10n.roomRecentsCreateEmptyRoom : VectorL10n.spacesAddRoom,
                 image: UIImage(systemName: "number"),
                 attributes: isAddRoomAvailable ? [] : .disabled) { [weak self] action in
            guard let self = self else { return }
            
            self.delegate?.allChatsEditActionProvider(self, didSelect: .createRoom)
        }
    }
    
    private var startChatAction: UIAction {
        UIAction(title: VectorL10n.roomRecentsStartChatWith,
                 image: UIImage(systemName: "person")) { [weak self] action in
            guard let self = self else { return }
            
            self.delegate?.allChatsEditActionProvider(self, didSelect: .startChat)
        }
    }
    
    private var createSpaceAction: UIAction {
        UIAction(title: parentSpace == nil ? VectorL10n.spacesCreateSpaceTitle : VectorL10n.spacesCreateSubspaceTitle,
                 image: UIImage(systemName: "plus"),
                 attributes: isAddRoomAvailable ? [] : .disabled) { [weak self] action in
            guard let self = self else { return }
            
            self.delegate?.allChatsEditActionProvider(self, didSelect: .createSpace)
        }
    }
}
