require 'active_support/core_ext/numeric/bytes'
require 'thor'
require 'table_print'
require 'debci/package'

module Debci
  class StorageLimit
    def storage_limit
      @storage_limit ||= Debci.config.disk_storage_limit.megabytes
    end

    def packages_with_excess_storage(all: false)
      expiration = Time.now - Debci.config.data_retention.days
      query = "
        SELECT packages.*, sum(jobs.log_size + jobs.artifacts_size) AS storage_used
        FROM packages
        JOIN jobs ON packages.id = jobs.package_id
        WHERE jobs.date > '#{expiration}' AND NOT jobs.files_purged
        GROUP BY packages.id
      "

      unless all
        query += "
        HAVING sum(jobs.log_size + jobs.artifacts_size) >= (CASE WHEN packages.storage_limit IS NOT NULL THEN packages.storage_limit ELSE #{storage_limit} END)
      "
      end

      Debci::Package.find_by_sql(query)
    end

    def run
      packages_with_excess_storage.each do |package|
        self.cleanup_package(package)
      end
    end

    def cleanup_package(package)
      storage = 0
      limit = package.storage_limit || storage_limit
      package.jobs.where(files_purged: false).order("date DESC").in_batches.each do |subset|
        subset.each do |job|
          storage += job.disk_usage
          if storage > limit
            job.cleanup(reason: "package taking too much disk space")
          end
        end
      end
    end

    class CLI < Thor
      include ActiveSupport::NumberHelper

      desc 'start', 'keeps storage limit by package/suite/architecture'
      def start
        ::Debci::StorageLimit.new.run
      end

      desc 'list', 'Lists packages that are exceeding their storage limit'
      option :all, type: :boolean, default: false, aliases: ["-a"], desc: "Lists all packages, not only exceeding ones"
      def list
        limit = ::Debci::StorageLimit.new
        packages = limit.packages_with_excess_storage(options[:all])
        used = ->(p) { number_to_human_size(p.storage_used) }
        max = ->(p) { number_to_human_size(p.storage_limit || limit.storage_limit) }
        tp packages, :name, { used: used }, { limit: max }
      end

      desc 'cleanup', 'Cleans up a single package'
      def cleanup(pkg)
        package = Debci::Package.find_by_name(pkg)
        limit = ::Debci::StorageLimit.new
        limit.cleanup_package(package)
      end
      default_task :start
    end
  end
end