--- Unit tests for pure functions in assume.lua
-- These tests focus on functions without external dependencies

-- Add the parent directory to the Lua path
local script_path = debug.getinfo(1, "S").source:sub(2)
local plugin_dir = script_path:match("(.*/)")
plugin_dir = plugin_dir:gsub("spec/$", "")

-- Mock the dependencies before requiring the module
_G.kong = {
  log = {
    notice = function() end,
    err = function() end,
  }
}

_G.ngx = {
  escape_uri = function(str)
    return str
  end
}

-- Mock resty.lock
package.loaded["resty.lock"] = {
  new = function()
    return {
      lock = function() return true end,
      unlock = function() return true end,
    }
  end
}

-- Mock resty.lrucache
package.loaded["resty.lrucache"] = {
  new = function()
    local cache = {}
    return {
      get = function(self, key) return cache[key] end,
      set = function(self, key, value) cache[key] = value end,
      delete = function(self, key) cache[key] = nil end,
      flush_all = function(self) cache = {} end,
    }
  end
}

package.loaded["resty.http"] = {}
package.loaded["kong.plugins.o11y-proxy.http_client"] = {
  new = function()
    return {
      set_timeout = function() end,
      request_uri = function() return {status = 200, body = ""}, nil end,
    }
  end
}
package.loaded["kong.plugins.o11y-proxy.irsa"] = {
  new = function()
    return {
      get_credentials = function()
        return {
          access_key_id = "test_key",
          secret_access_key = "test_secret",
          session_token = "test_token",
        }
      end
    }
  end
}
package.loaded["kong.plugins.o11y-proxy.utils"] = {
  parse_iso8601 = function(date_str)
    -- Simple mock that returns a future timestamp for valid dates
    if date_str and date_str ~= "" then
      return os.time() + 3600  -- 1 hour in the future
    end
    return nil
  end
}
package.loaded["kong.plugins.o11y-proxy.aws_signer"] = {
  sign_request = function()
    return {}, nil
  end
}

local assume = dofile(plugin_dir .. "assume.lua")

describe("assume.lua - pure functions", function()
  
  describe("parse_sts_assume_role_response", function()
    local parse_fn = assume._parse_sts_assume_role_response
    
    it("should parse valid STS AssumeRole XML response", function()
      local xml_body = [[
        <AssumeRoleResponse xmlns="https://sts.amazonaws.com/doc/2011-06-15/">
          <AssumeRoleResult>
            <Credentials>
              <AccessKeyId>ASIATESTACCESSKEY</AccessKeyId>
              <SecretAccessKey>TestSecretKey123456789</SecretAccessKey>
              <SessionToken>TestSessionToken123456789</SessionToken>
              <Expiration>2024-01-01T12:00:00Z</Expiration>
            </Credentials>
          </AssumeRoleResult>
        </AssumeRoleResponse>
      ]]
      
      local credentials, err = parse_fn(xml_body)
      
      assert.is_nil(err)
      assert.is_not_nil(credentials)
      assert.equals("ASIATESTACCESSKEY", credentials.access_key_id)
      assert.equals("TestSecretKey123456789", credentials.secret_access_key)
      assert.equals("TestSessionToken123456789", credentials.session_token)
      assert.equals("2024-01-01T12:00:00Z", credentials.expiration)
    end)
    
    it("should return error for nil xml_body", function()
      local credentials, err = parse_fn(nil)
      
      assert.is_nil(credentials)
      assert.equals("Empty response body", err)
    end)
    
    it("should return error for empty xml_body", function()
      local credentials, err = parse_fn("")
      
      assert.is_nil(credentials)
      -- Empty string will fail at parsing stage, not the initial check
      assert.equals("Failed to parse credentials from STS response", err)
    end)
    
    it("should return error when AccessKeyId is missing", function()
      local xml_body = [[
        <AssumeRoleResponse>
          <AssumeRoleResult>
            <Credentials>
              <SecretAccessKey>TestSecretKey</SecretAccessKey>
              <SessionToken>TestSessionToken</SessionToken>
              <Expiration>2024-01-01T12:00:00Z</Expiration>
            </Credentials>
          </AssumeRoleResult>
        </AssumeRoleResponse>
      ]]
      
      local credentials, err = parse_fn(xml_body)
      
      assert.is_nil(credentials)
      assert.equals("Failed to parse credentials from STS response", err)
    end)
    
    it("should return error when SecretAccessKey is missing", function()
      local xml_body = [[
        <AssumeRoleResponse>
          <AssumeRoleResult>
            <Credentials>
              <AccessKeyId>ASIATESTACCESSKEY</AccessKeyId>
              <SessionToken>TestSessionToken</SessionToken>
              <Expiration>2024-01-01T12:00:00Z</Expiration>
            </Credentials>
          </AssumeRoleResult>
        </AssumeRoleResponse>
      ]]
      
      local credentials, err = parse_fn(xml_body)
      
      assert.is_nil(credentials)
      assert.equals("Failed to parse credentials from STS response", err)
    end)
    
    it("should return error when SessionToken is missing", function()
      local xml_body = [[
        <AssumeRoleResponse>
          <AssumeRoleResult>
            <Credentials>
              <AccessKeyId>ASIATESTACCESSKEY</AccessKeyId>
              <SecretAccessKey>TestSecretKey</SecretAccessKey>
              <Expiration>2024-01-01T12:00:00Z</Expiration>
            </Credentials>
          </AssumeRoleResult>
        </AssumeRoleResponse>
      ]]
      
      local credentials, err = parse_fn(xml_body)
      
      assert.is_nil(credentials)
      assert.equals("Failed to parse credentials from STS response", err)
    end)
    
    it("should parse response even when Expiration is missing", function()
      local xml_body = [[
        <AssumeRoleResponse>
          <AssumeRoleResult>
            <Credentials>
              <AccessKeyId>ASIATESTACCESSKEY</AccessKeyId>
              <SecretAccessKey>TestSecretKey</SecretAccessKey>
              <SessionToken>TestSessionToken</SessionToken>
            </Credentials>
          </AssumeRoleResult>
        </AssumeRoleResponse>
      ]]
      
      local credentials, err = parse_fn(xml_body)
      
      -- Should still succeed as expiration is optional in the parsing logic
      assert.is_nil(err)
      assert.is_not_nil(credentials)
      assert.equals("ASIATESTACCESSKEY", credentials.access_key_id)
      assert.equals("TestSecretKey", credentials.secret_access_key)
      assert.equals("TestSessionToken", credentials.session_token)
      assert.is_nil(credentials.expiration)
    end)
    
    it("should handle XML with extra whitespace", function()
      local xml_body = [[
        <AssumeRoleResponse>
          <AssumeRoleResult>
            <Credentials>
              <AccessKeyId>  ASIATESTACCESSKEY  </AccessKeyId>
              <SecretAccessKey>  TestSecretKey  </SecretAccessKey>
              <SessionToken>  TestSessionToken  </SessionToken>
              <Expiration>  2024-01-01T12:00:00Z  </Expiration>
            </Credentials>
          </AssumeRoleResult>
        </AssumeRoleResponse>
      ]]
      
      local credentials, err = parse_fn(xml_body)
      
      assert.is_nil(err)
      assert.is_not_nil(credentials)
      -- Note: Pattern matching will include whitespace
      assert.equals("  ASIATESTACCESSKEY  ", credentials.access_key_id)
    end)
    
    it("should handle credentials with special characters", function()
      local xml_body = [[
        <AssumeRoleResponse>
          <AssumeRoleResult>
            <Credentials>
              <AccessKeyId>ASIA+TEST/KEY=</AccessKeyId>
              <SecretAccessKey>Secret+Key/123=</SecretAccessKey>
              <SessionToken>Token+123/ABC=</SessionToken>
              <Expiration>2024-01-01T12:00:00Z</Expiration>
            </Credentials>
          </AssumeRoleResult>
        </AssumeRoleResponse>
      ]]
      
      local credentials, err = parse_fn(xml_body)
      
      assert.is_nil(err)
      assert.is_not_nil(credentials)
      assert.equals("ASIA+TEST/KEY=", credentials.access_key_id)
      assert.equals("Secret+Key/123=", credentials.secret_access_key)
      assert.equals("Token+123/ABC=", credentials.session_token)
    end)
    
    it("should handle malformed XML gracefully", function()
      local xml_body = "<InvalidXML><NoCredentials></InvalidXML>"
      
      local credentials, err = parse_fn(xml_body)
      
      assert.is_nil(credentials)
      assert.equals("Failed to parse credentials from STS response", err)
    end)
  end)
  
  describe("is_assume_cache_valid", function()
    local is_cache_valid_fn = assume._is_assume_cache_valid
    local assume_obj
    
    before_each(function()
      -- Create a fresh assume object for each test
      assume_obj = assume.new({
        role_arn = "arn:aws:iam::123456789012:role/TestRole"
      })
      -- Clear cache before each test
      assume_obj:clear_cache()
    end)
    
    it("should return false when cache is empty", function()
      local is_valid = is_cache_valid_fn("arn:aws:iam::123456789012:role/TestRole")
      assert.is_false(is_valid)
    end)
  end)
end)
