class Kamal::Secrets::Adapters::OnePassword < Kamal::Secrets::Adapters::Base
  delegate :optionize, to: Kamal::Utils

  private
    def login(account)
      unless loggedin?(account)
        `op signin #{to_options(account: account, force: true, raw: true)}`.tap do
          raise RuntimeError, "Failed to login to 1Password" unless $?.success?
        end
      end
    end

    def loggedin?(account)
      `op account get --account #{account.shellescape} 2> /dev/null`
      $?.success?
    end

    def fetch_secrets(secrets, account:, session:)
      {}.tap do |results|
        vaults_items_fields(secrets).map do |vault, items|
          items.each do |item, fields|
            fields_json = JSON.parse(op_item_get(vault, item, fields, account: account, session: session))
            fields_json = [ fields_json ] if fields.one?

            fields_json.each do |field_json|
              # The reference is in the form `op://vault/item/field[/field]`
              field = field_json["reference"].delete_prefix("op://").delete_suffix("/password")
              results[field] = field_json["value"]
            end
          end
        end
      end
    end

    def to_options(**options)
      optionize(options.compact).join(" ")
    end

    def vaults_items_fields(secrets)
      {}.tap do |vaults|
        secrets.each do |secret|
          secret = secret.delete_prefix("op://")
          vault, item, *fields = secret.split("/")
          fields << "password" if fields.empty?

          vaults[vault] ||= {}
          vaults[vault][item] ||= []
          vaults[vault][item] << fields.join(".")
        end
      end
    end

    def op_item_get(vault, item, fields, account:, session:)
      labels = fields.map { |field| "label=#{field}" }.join(",")
      options = to_options(vault: vault, fields: labels, format: "json", account: account, session: session.presence)

      `op item get #{item.shellescape} #{options}`.tap do
        raise RuntimeError, "Could not read #{fields.join(", ")} from #{item} in the #{vault} 1Password vault" unless $?.success?
      end
    end
end