// k6 HTTP Authentication
// Support for various authentication methods

///| Authentication Types

///|
/// Basic Authentication credentials
pub struct BasicAuth {
  username : String
  password : String
}

///|
/// Create Basic Auth
pub fn BasicAuth::new(username : String, password : String) -> BasicAuth {
  { username, password }
}

///|
/// Bearer Token Authentication
pub struct BearerAuth {
  token : String
}

///|
/// Create Bearer Auth
pub fn BearerAuth::new(token : String) -> BearerAuth {
  { token, }
}

///|
/// API Key Authentication
pub struct ApiKeyAuth {
  key : String
  header_name : String
}

///|
/// Create API Key Auth
pub fn ApiKeyAuth::new(key : String, header_name : String) -> ApiKeyAuth {
  { key, header_name }
}

///| Request Builder Extensions for Auth

///|
/// Add Basic Authentication to request
pub fn RequestBuilder::basic_auth(
  self : RequestBuilder,
  username : String,
  password : String,
) -> RequestBuilder {
  // Encode credentials as base64
  let credentials = username + ":" + password
  let encoded = b64encode(credentials)
  self.header("Authorization", "Basic " + encoded)
}

///|
/// Add Bearer token authentication
pub fn RequestBuilder::bearer_auth(
  self : RequestBuilder,
  token : String,
) -> RequestBuilder {
  self.header("Authorization", "Bearer " + token)
}

///|
/// Add API Key authentication
pub fn RequestBuilder::api_key_auth(
  self : RequestBuilder,
  key : String,
  header_name : String,
) -> RequestBuilder {
  self.header(header_name, key)
}

///|
/// Add custom authentication header
pub fn RequestBuilder::custom_auth(
  self : RequestBuilder,
  header_value : String,
) -> RequestBuilder {
  self.header("Authorization", header_value)
}

///| Authentication Helpers

///|
/// Create Authorization header for Basic Auth
pub fn create_basic_auth_header(username : String, password : String) -> String {
  let credentials = username + ":" + password
  let encoded = b64encode(credentials)
  "Basic " + encoded
}

///|
/// Create Authorization header for Bearer token
pub fn create_bearer_auth_header(token : String) -> String {
  "Bearer " + token
}

///|
/// Create API Key header value
pub fn create_api_key_header(key : String) -> String {
  key
}

///| OAuth 2.0 Helpers

///|
/// OAuth 2.0 token response
pub struct OAuthToken {
  access_token : String
  token_type : String
  expires_in : Int?
  refresh_token : String?
}

///|
/// Request OAuth 2.0 token (Client Credentials flow)
pub fn request_oauth_token(
  token_url : String,
  client_id : String,
  client_secret : String,
) -> OAuthToken {
  // Create form data
  let body = @core.new_object()
  body["grant_type"] = @core.any("client_credentials")
  body["client_id"] = @core.any(client_id)
  body["client_secret"] = @core.any(client_secret)

  // Make request
  let res = RequestBuilder::post(token_url)
    .header("Content-Type", "application/x-www-form-urlencoded")
    .json(@core.any(body))
    .send()

  // Parse response
  let json = res.json
  let access_token : String = json["access_token"].cast()
  let token_type : String = json["token_type"].cast()

  { access_token, token_type, expires_in: None, refresh_token: None }
}

///|
/// Use OAuth token with request
pub fn RequestBuilder::oauth_token(
  self : RequestBuilder,
  token : OAuthToken,
) -> RequestBuilder {
  let auth_value = token.token_type + " " + token.access_token
  self.header("Authorization", auth_value)
}

///| Session Management

///|
/// Session with authentication
pub struct AuthSession {
  auth_type : String
  credentials : @core.Any
  headers : Map[String, String]
}

///|
/// Create session with Basic Auth
pub fn AuthSession::basic(username : String, password : String) -> AuthSession {
  let headers = Map::new()
  let auth_header = create_basic_auth_header(username, password)
  headers.set("Authorization", auth_header)

  let creds = @core.new_object()
  creds["username"] = @core.any(username)
  creds["password"] = @core.any(password)

  { auth_type: "basic", credentials: @core.any(creds), headers }
}

///|
/// Create session with Bearer token
pub fn AuthSession::bearer(token : String) -> AuthSession {
  let headers = Map::new()
  headers.set("Authorization", "Bearer " + token)

  let creds = @core.new_object()
  creds["token"] = @core.any(token)

  { auth_type: "bearer", credentials: @core.any(creds), headers }
}

///|
/// Create session with API Key
pub fn AuthSession::api_key(key : String, header_name : String) -> AuthSession {
  let headers = Map::new()
  headers.set(header_name, key)

  let creds = @core.new_object()
  creds["key"] = @core.any(key)
  creds["headerName"] = @core.any(header_name)

  { auth_type: "apikey", credentials: @core.any(creds), headers }
}

///|
/// Apply session auth to request builder
pub fn RequestBuilder::with_session(
  self : RequestBuilder,
  session : AuthSession,
) -> RequestBuilder {
  self.headers(session.headers)
}

///| Authentication Testing Helpers

///|
/// Test if authentication is required
pub fn test_auth_required(url : String) -> Bool {
  let res = quick_get(url)
  res.status == 401 || res.status == 403
}

///|
/// Test if authentication is valid
pub fn test_auth_valid(url : String, session : AuthSession) -> Bool {
  let res = RequestBuilder::get(url).with_session(session).send()
  res.status == 200
}

///|
/// Test Basic Auth credentials
pub fn test_basic_auth(
  url : String,
  username : String,
  password : String,
) -> Bool {
  let res = RequestBuilder::get(url).basic_auth(username, password).send()
  res.status == 200
}

///|
/// Test Bearer token
pub fn test_bearer_token(url : String, token : String) -> Bool {
  let res = RequestBuilder::get(url).bearer_auth(token).send()
  res.status == 200
}