require File.expand_path '../test_helper', __dir__
require 'azure/storage/common'
require 'base64'
require 'uri'

class TestSharedAccessSignatureGeneration < Minitest::Test
  def setup
    @access_account_name = 'testaccount'
    @access_key_base64 = Base64.strict_encode64('test-access-key')
    @path = '/container/blob.txt'
    @service_type = Azure::Storage::Common::ServiceType::BLOB

    @service_options = {
      service: 'b',
      permissions: 'c',
      start: '2025-09-10T17:00:00Z',
      expiry: '2025-09-10T18:00:00Z',
      resource: 'b',
      protocol: 'https',
      ip_range: '192.168.1.1-192.168.1.10'
    }

    @user_delegation_key = Azure::Storage::Common::Service::UserDelegationKey.new do |key|
      key.signed_oid = 'user-object-id-123'
      key.signed_tid = 'tenant-id-456'
      key.signed_start = '2025-09-10T17:00:00Z'
      key.signed_expiry = '2025-09-10T19:00:00Z'
      key.signed_service = 'b'
      key.signed_version = '2020-08-04'
      key.value = Base64.strict_encode64('user-delegation-key-value')
    end
  end

  def test_service_sas_signature_string_format
    # Test regular service SAS (no user delegation key)
    sas = Azure::Storage::Common::Core::Auth::SharedAccessSignature.new(@access_account_name, @access_key_base64)

    result = sas.signable_string_for_service(@service_type, @path, @service_options)
    lines = result.split("\n", -1) # Preserve training newlines

    # Verify service SAS structure
    assert_equal 'c', lines[0] # permissions
    assert_equal '2025-09-10T17:00:00Z', lines[1]                # start
    assert_equal '2025-09-10T18:00:00Z', lines[2]                # expiry
    assert_equal '/blob/testaccount/container/blob.txt', lines[3] # canonicalized resource
    assert_equal '', lines[4]                                     # identifier (empty for service SAS)
    assert_equal '192.168.1.1-192.168.1.10', lines[5] # ip_range
    assert_equal 'https', lines[6] # protocol
    assert_equal Azure::Storage::Common::Default::STG_VERSION, lines[7] # version
    assert_equal 'b', lines[8]                                    # resource
    assert_equal '', lines[9]                                     # timestamp (empty)
    assert_equal '', lines[10]                                    # encryption scope (empty)
    # Response headers (all empty)
    assert_equal '', lines[11]                                    # cache_control
    assert_equal '', lines[12]                                    # content_disposition
    assert_equal '', lines[13]                                    # content_encoding
    assert_equal '', lines[14]                                    # content_language
    assert_equal '', lines[15]                                    # content_type
  end

  def test_user_delegation_sas_signature_string_format
    # Test user delegation SAS - always includes new 2025-07-05 format
    sas = Azure::Storage::Common::Core::Auth::SharedAccessSignature.new(@access_account_name, @access_key_base64, @user_delegation_key)

    result = sas.signable_string_for_service(@service_type, @path, @service_options)
    lines = result.split("\n", -1) # Preserve training newlines

    # Verify user delegation SAS structure
    assert_equal 'c', lines[0] # permissions
    assert_equal '2025-09-10T17:00:00Z', lines[1]                # start
    assert_equal '2025-09-10T18:00:00Z', lines[2]                # expiry
    assert_equal '/blob/testaccount/container/blob.txt', lines[3] # canonicalized resource

    # User delegation key fields
    assert_equal 'user-object-id-123', lines[4]                  # signed_oid
    assert_equal 'tenant-id-456', lines[5]                       # signed_tid
    assert_equal '2025-09-10T17:00:00Z', lines[6]                # signed_start
    assert_equal '2025-09-10T19:00:00Z', lines[7]                # signed_expiry
    assert_equal 'b', lines[8]                                   # signed_service
    assert_equal '2020-08-04', lines[9]                          # signed_version

    # New fields for user delegation SAS (empty by default)
    assert_equal '', lines[10]                                   # saoid
    assert_equal '', lines[11]                                   # suoid
    assert_equal '', lines[12]                                   # scid

    # Critical: Two empty lines - this was causing the signature mismatch!
    assert_equal '', lines[13]                                   # First empty line
    assert_equal '', lines[14]                                   # Second empty line

    # Continue with remaining fields
    assert_equal '192.168.1.1-192.168.1.10', lines[15] # ip_range
    assert_equal 'https', lines[16] # protocol
    assert_equal Azure::Storage::Common::Default::STG_VERSION, lines[17] # version
    assert_equal 'b', lines[18]                                  # resource
    assert_equal '', lines[19]                                   # timestamp
    assert_equal '', lines[20]                                   # encryption scope
    # Response headers (all empty)
    assert_equal '', lines[21]                                   # cache_control
    assert_equal '', lines[22]                                   # content_disposition
    assert_equal '', lines[23]                                   # content_encoding
    assert_equal '', lines[24]                                   # content_language
    assert_equal '', lines[25]                                   # content_type
  end

  def test_user_delegation_sas_with_optional_fields
    # Test user delegation SAS with optional fields set
    sas = Azure::Storage::Common::Core::Auth::SharedAccessSignature.new(@access_account_name, @access_key_base64, @user_delegation_key)

    options_with_extras = @service_options.merge({
                                                   signed_authorized_user_object_id: 'authorized-user-789',
                                                   signed_unauthorized_user_object_id: 'unauthorized-user-000',
                                                   signed_correlation_id: 'correlation-abc-123',
                                                   signed_encryption_scope: 'test-encryption-scope',
                                                   timestamp: '2025-09-10T17:30:00Z',
                                                   cache_control: 'no-cache',
                                                   content_type: 'application/json'
                                                 })

    result = sas.signable_string_for_service(@service_type, @path, options_with_extras)
    lines = result.split("\n", -1) # Preserve training newlines

    # Verify the optional fields are included
    assert_equal 'authorized-user-789', lines[10]               # saoid
    assert_equal 'unauthorized-user-000', lines[11]             # suoid
    assert_equal 'correlation-abc-123', lines[12]               # scid
    assert_equal '2025-09-10T17:30:00Z', lines[19]              # timestamp
    assert_equal 'test-encryption-scope', lines[20]             # encryption scope
    assert_equal 'no-cache', lines[21]                          # cache_control
    assert_equal 'application/json', lines[25]                  # content_type
  end

  def test_table_service_signature_format
    # Test table service (no blob-specific fields)
    sas = Azure::Storage::Common::Core::Auth::SharedAccessSignature.new(@access_account_name, @access_key_base64)

    table_options = {
      service: 't',
      permissions: 'rau',
      expiry: '2025-09-10T18:00:00Z',
      startpk: 'partition1',
      startrk: 'row1',
      endpk: 'partition2',
      endrk: 'row2'
    }

    result = sas.signable_string_for_service(Azure::Storage::Common::ServiceType::TABLE, 'mytable', table_options)
    lines = result.split("\n", -1) # Preserve training newlines

    # Table service should not have blob-specific fields
    assert_equal 'rau', lines[0]                                 # permissions
    assert_equal '/table/testaccount/mytable', lines[3]          # canonicalized resource

    # Should end with table-specific fields (no response headers for table)
    expected_line_count = 12 # Basic fields + table fields, no blob fields
    assert_equal expected_line_count, lines.length
    assert_equal 'partition1', lines[8]                          # startpk
    assert_equal 'row1', lines[9]                                # startrk
    assert_equal 'partition2', lines[10]                         # endpk
    assert_equal 'row2', lines[11]                               # endrk
  end

  def test_canonicalized_resource_format
    sas = Azure::Storage::Common::Core::Auth::SharedAccessSignature.new(@access_account_name, @access_key_base64)

    # Test with leading slash
    result = sas.canonicalized_resource('blob', '/container/blob.txt')
    assert_equal '/blob/testaccount/container/blob.txt', result

    # Test without leading slash
    result = sas.canonicalized_resource('blob', 'container/blob.txt')
    assert_equal '/blob/testaccount/container/blob.txt', result

    # Test table resource
    result = sas.canonicalized_resource('table', 'mytable')
    assert_equal '/table/testaccount/mytable', result
  end

  def test_sas_token_generation_creates_valid_query_string
    # Test that generate_service_sas_token creates a valid query string
    sas = Azure::Storage::Common::Core::Auth::SharedAccessSignature.new(@access_account_name, @access_key_base64)

    token = sas.generate_service_sas_token(@path, @service_options)

    # Parse the query string
    params = URI.decode_www_form(token).to_h

    # Verify expected parameters are present
    assert_equal 'c', params['sp'] # permissions
    assert_equal '2025-09-10T17:00:00Z', params['st']           # start
    assert_equal '2025-09-10T18:00:00Z', params['se']           # expiry
    assert_equal 'b', params['sr']                               # resource
    assert_equal 'https', params['spr']                          # protocol
    assert_equal '192.168.1.1-192.168.1.10', params['sip'] # ip_range
    assert params.key?('sig')                                    # signature should be present
    refute_empty params['sig']                                   # signature should not be empty
  end
end
