//===----------------------------------------------------------------------===//
// Copyright © 2026 Apple Inc. and the Containerization project authors.
//
// 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
//
//   https://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 Foundation

extension ProgressBar {
    /// A handler function to update the progress bar.
    /// - Parameter events: The events to handle.
    public func handler(_ events: [ProgressUpdateEvent]) {
        for event in events {
            switch event {
            case .setDescription(let description):
                set(description: description)
            case .setSubDescription(let subDescription):
                set(subDescription: subDescription)
            case .setItemsName(let itemsName):
                set(itemsName: itemsName)
            case .addTasks(let tasks):
                add(tasks: tasks)
            case .setTasks(let tasks):
                set(tasks: tasks)
            case .addTotalTasks(let totalTasks):
                add(totalTasks: totalTasks)
            case .setTotalTasks(let totalTasks):
                set(totalTasks: totalTasks)
            case .addSize(let size):
                add(size: size)
            case .setSize(let size):
                set(size: size)
            case .addTotalSize(let totalSize):
                add(totalSize: totalSize)
            case .setTotalSize(let totalSize):
                set(totalSize: totalSize)
            case .addItems(let items):
                add(items: items)
            case .setItems(let items):
                set(items: items)
            case .addTotalItems(let totalItems):
                add(totalItems: totalItems)
            case .setTotalItems(let totalItems):
                set(totalItems: totalItems)
            case .custom:
                // Custom events are handled by the client.
                break
            }
        }
    }

    /// Performs a check to see if the progress bar should be finished.
    public func checkIfFinished() {
        let state = self.state.withLock { $0 }

        var finished = true
        var defined = false
        if let totalTasks = state.totalTasks, totalTasks > 0 {
            // For tasks, we're showing the current task rather than the number of completed tasks.
            finished = finished && state.tasks == totalTasks
            defined = true
        }
        if let totalItems = state.totalItems, totalItems > 0 {
            finished = finished && state.items == totalItems
            defined = true
        }
        if let totalSize = state.totalSize, totalSize > 0 {
            finished = finished && state.size == totalSize
            defined = true
        }
        if defined && finished {
            finish()
        }
    }

    /// Sets the current tasks.
    /// - Parameter newTasks: The current tasks to set.
    /// - Parameter render: The flag indicating whether the progress bar has to render after the update.
    public func set(tasks newTasks: Int, render: Bool = true) {
        state.withLock { $0.tasks = newTasks }
        if render {
            self.render()
        }
        checkIfFinished()
    }

    /// Performs an addition to the current tasks.
    /// - Parameter delta: The tasks to add to the current tasks.
    /// - Parameter render: The flag indicating whether the progress bar has to render after the update.
    public func add(tasks delta: Int, render: Bool = true) {
        state.withLock {
            let newTasks = $0.tasks + delta
            $0.tasks = newTasks
        }
        if render {
            self.render()
        }
    }

    /// Sets the total tasks.
    /// - Parameter newTotalTasks: The total tasks to set.
    /// - Parameter render: The flag indicating whether the progress bar has to render after the update.
    public func set(totalTasks newTotalTasks: Int, render: Bool = true) {
        state.withLock { $0.totalTasks = newTotalTasks }
        if render {
            self.render()
        }
    }

    /// Performs an addition to the total tasks.
    /// - Parameter delta: The tasks to add to the total tasks.
    /// - Parameter render: The flag indicating whether the progress bar has to render after the update.
    public func add(totalTasks delta: Int, render: Bool = true) {
        state.withLock {
            let totalTasks = $0.totalTasks ?? 0
            let newTotalTasks = totalTasks + delta
            $0.totalTasks = newTotalTasks
        }
        if render {
            self.render()
        }
    }

    /// Sets the items name.
    /// - Parameter newItemsName: The current items to set.
    /// - Parameter render: The flag indicating whether the progress bar has to render after the update.
    public func set(itemsName newItemsName: String, render: Bool = true) {
        state.withLock { $0.itemsName = newItemsName }
        if render {
            self.render()
        }
    }

    /// Sets the current items.
    /// - Parameter newItems: The current items to set.
    public func set(items newItems: Int, render: Bool = true) {
        state.withLock { $0.items = newItems }
        if render {
            self.render()
        }
    }

    /// Performs an addition to the current items.
    /// - Parameter delta: The items to add to the current items.
    /// - Parameter render: The flag indicating whether the progress bar has to render after the update.
    public func add(items delta: Int, render: Bool = true) {
        state.withLock {
            let newItems = $0.items + delta
            $0.items = newItems
        }
        if render {
            self.render()
        }
    }

    /// Sets the total items.
    /// - Parameter newTotalItems: The total items to set.
    /// - Parameter render: The flag indicating whether the progress bar has to render after the update.
    public func set(totalItems newTotalItems: Int, render: Bool = true) {
        state.withLock { $0.totalItems = newTotalItems }
        if render {
            self.render()
        }
    }

    /// Performs an addition to the total items.
    /// - Parameter delta: The items to add to the total items.
    /// - Parameter render: The flag indicating whether the progress bar has to render after the update.
    public func add(totalItems delta: Int, render: Bool = true) {
        state.withLock {
            let totalItems = $0.totalItems ?? 0
            let newTotalItems = totalItems + delta
            $0.totalItems = newTotalItems
        }
        if render {
            self.render()
        }
    }

    /// Sets the current size.
    /// - Parameter newSize: The current size to set.
    /// - Parameter render: The flag indicating whether the progress bar has to render after the update.
    public func set(size newSize: Int64, render: Bool = true) {
        state.withLock { $0.size = newSize }
        if render {
            self.render()
        }
    }

    /// Performs an addition to the current size.
    /// - Parameter delta: The size to add to the current size.
    /// - Parameter render: The flag indicating whether the progress bar has to render after the update.
    public func add(size delta: Int64, render: Bool = true) {
        state.withLock {
            let newSize = $0.size + delta
            $0.size = newSize
        }
        if render {
            self.render()
        }
    }

    /// Sets the total size.
    /// - Parameter newTotalSize: The total size to set.
    /// - Parameter render: The flag indicating whether the progress bar has to render after the update.
    public func set(totalSize newTotalSize: Int64, render: Bool = true) {
        state.withLock { $0.totalSize = newTotalSize }
        if render {
            self.render()
        }
    }

    /// Performs an addition to the total size.
    /// - Parameter delta: The size to add to the total size.
    /// - Parameter render: The flag indicating whether the progress bar has to render after the update.
    public func add(totalSize delta: Int64, render: Bool = true) {
        state.withLock {
            let totalSize = $0.totalSize ?? 0
            let newTotalSize = totalSize + delta
            $0.totalSize = newTotalSize
        }
        if render {
            self.render()
        }
    }
}
