#!/usr/bin/env ruby

# -------------------------------------------------------------------------- #
# Copyright 2002-2026, OpenNebula Project, OpenNebula Systems                #
#                                                                            #
# 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.                                             #
#--------------------------------------------------------------------------- #

ONE_LOCATION = ENV['ONE_LOCATION']

if !ONE_LOCATION
    LIB_LOCATION      = '/usr/lib/one'
    RUBY_LIB_LOCATION = '/usr/lib/one/ruby'
    GEMS_LOCATION     = '/usr/share/one/gems'
else
    LIB_LOCATION      = ONE_LOCATION + '/lib'
    RUBY_LIB_LOCATION = ONE_LOCATION + '/lib/ruby'
    GEMS_LOCATION     = ONE_LOCATION + '/share/gems'
end

require 'load_opennebula_paths'

$LOAD_PATH << RUBY_LIB_LOCATION
$LOAD_PATH << RUBY_LIB_LOCATION + '/cli'

USER_AGENT = 'CLI'

require 'command_parser'
require 'one_helper'
require 'opennebula/oneform_client'

require 'one_helper/oneprovider_helper'

CommandParser::CmdParser.new(ARGV) do
    FORMAT = [OpenNebulaHelper::JSON, OpenNebulaHelper::YAML]

    usage '`oneprovider` <command> [<args>] [<options>]'
    version OpenNebulaHelper::ONE_VERSION

    set :option, OneForm::Client::DEFAULT_OPTIONS
    set :option, CommandParser::VERSION
    set :option, CommandParser::HELP

    SENSITIVE = {
        :name => 'sensitive',
        :short => '-s',
        :large => '--sensitive',
        :description => 'Include sensitive values information in the output',
        :default => false
    }

    ENABLED = {
        :name => 'enabled',
        :short => '-e',
        :large => '--enabled',
        :description => 'Show only enabled providers',
        :default => false
    }

    helper = OneProviderHelper.new

    ############################################################################
    # Formatters for arguments
    ############################################################################
    set :format, :groupid, OpenNebulaHelper.rname_to_id_desc('GROUP') do |arg|
        OpenNebulaHelper.rname_to_id(arg, 'GROUP')
    end

    set :format, :userid, OpenNebulaHelper.rname_to_id_desc('USER') do |arg|
        OpenNebulaHelper.rname_to_id(arg, 'USER')
    end

    set :format, :templateid, OneForm::Helpers.rname_to_id_desc('PROVIDER') do |arg|
        OneForm::Helpers.rname_to_id(arg, 'PROVIDER')
    end

    set :format, :templateid_list, OneForm::Helpers.list_to_id_desc('PROVIDER') do |arg|
        OneForm::Helpers.list_to_id(arg, 'PROVIDER')
    end

    ########################################################################
    # Commands for interacting with provider templates
    ########################################################################

    list_desc = <<-EOT.unindent
        List all providers.
    EOT

    command :list,
            list_desc,
            :options => (
                FORMAT + CLIHelper::OPTIONS +
                [OpenNebulaHelper::DESCRIBE, SENSITIVE, ENABLED]
            ) do
        helper.list(helper.client(options), options)
    rescue Interrupt
        puts
        exit(1)
    end

    top_desc = <<-EOT.unindent
        List the available providers continuously
    EOT

    command :top,
            top_desc,
            :options => FORMAT + [CLIHelper::DELAY, SENSITIVE, ENABLED] do
        Signal.trap('INT') { exit(1) }
        helper.top(helper.client(options), options)
    rescue Interrupt
        puts
        exit(1)
    end

    show_desc = <<-EOT.unindent
        Show details of a specific provider template.
    EOT

    command :show, show_desc, :provider_id, :options => FORMAT + [SENSITIVE] do
        provider_id = args[0]

        if provider_id.to_s !~ /^\d+$/
            STDERR.puts 'Invalid provider ID'
            exit(-1)
        end

        provider_id = provider_id.to_i

        helper.show(helper.client(options), provider_id, options)
    rescue Interrupt
        puts
        exit(1)
    end

    create_desc = <<-EOT.unindent
        Create a Provider object based on driver information.
    EOT

    command :create, create_desc, :driver_name, [:file, nil] do
        driver_name = args[0]
        file_path   = args[1]

        helper.create(helper.client(options), driver_name, file_path)
    rescue Interrupt
        puts
        exit(1)
    end

    update_desc = <<-EOT.unindent
        Update a provider template by ID with the provided patch data.
    EOT

    command :update, update_desc, :provider_id, [:file, nil] do
        provider_id = args[0].to_i
        file_path   = args[1]

        helper.update(helper.client(options), provider_id, file_path)
    rescue Interrupt
        puts
        exit(1)
    end

    rename_desc = <<-EOT.unindent
        Renames the Provider
    EOT

    command :rename, rename_desc, :provider_id, :name do
        provider_id = args[0].to_i
        name        = args[1]

        if name.nil? || name.empty?
            STDERR.puts 'Name cannot be empty'
            exit(-1)
        end

        helper.rename(helper.client(options), provider_id, name)
    rescue Interrupt
        puts
        exit(1)
    end

    chgrp_desc = <<-EOT.unindent
        Change the group of a provider.
    EOT

    command :chgrp, chgrp_desc, [:range, :providerid_list], :groupid do
        provider_ids = args[0]
        group_id     = args[1]

        if group_id.to_s !~ /^\d+$/
            STDERR.puts 'Invalid group ID'
            exit(-1)
        end

        group_id = group_id.to_i

        helper.chgrp(helper.client(options), provider_ids, group_id)
    rescue Interrupt
        puts
        exit(1)
    end

    chown_desc = <<-EOT.unindent
        Change the owner of a provider.
    EOT

    command :chown,
            chown_desc,
            [:range, :providerid_list],
            :userid,
            [:groupid, nil] do
        provider_ids = args[0]
        user_id      = args[1]
        group_id     = args[2]

        if user_id.to_s !~ /^\d+$/
            STDERR.puts 'Invalid user ID'
            exit(-1)
        end

        if group_id && group_id.to_s !~ /^\d+$/
            STDERR.puts 'Invalid group ID'
            exit(-1)
        end

        user_id  = user_id.to_i
        group_id = group_id&.to_i

        helper.chown(helper.client(options), provider_ids, user_id, group_id)
    rescue Interrupt
        puts
        exit(1)
    end

    chmod_desc = <<-EOT.unindent
        Change the permissions of a provider.
    EOT

    command :chmod, chmod_desc, [:range, :providerid_list], :octet do
        provider_ids = args[0]
        octet        = args[1]

        if !/\A\d+\z/.match(octet)
            STDERR.puts "Invalid '#{octet}' octed permissions"
            exit(-1)
        end

        permissions = OpenNebulaHelper.to_octet(octet)
        helper.chmod(helper.client(options), provider_ids, permissions)
    rescue Interrupt
        puts
        exit(1)
    end

    delete_desc = <<-EOT.unindent
        Delete a provider template by ID.
    EOT

    command :delete, delete_desc, [:range, :providerid_list] do
        provider_ids = args[0]

        helper.delete(helper.client(options), provider_ids)
    rescue Interrupt
        puts
        exit(1)
    end
end
