///| Token Request Implementation

///|
/// TokenRequest represents an OAuth2 token request
/// Used to exchange authorization code for access token
pub struct TokenRequest {
  token_url : TokenUrl
  client_id : ClientId
  client_secret : ClientSecret
  code : String
  redirect_uri : RedirectUrl
  grant_type : String
  pkce_verifier : PkceCodeVerifier?
} derive(Show)

///|
/// Create a new TokenRequest for authorization code grant
pub fn TokenRequest::new(
  token_url : TokenUrl,
  client_id : ClientId,
  client_secret : ClientSecret,
  code : String,
  redirect_uri : RedirectUrl,
) -> TokenRequest {
  {
    token_url,
    client_id,
    client_secret,
    code,
    redirect_uri,
    grant_type: "authorization_code",
    pkce_verifier: None,
  }
}

///|
/// Create a new TokenRequest with PKCE support
pub fn TokenRequest::new_with_pkce(
  token_url : TokenUrl,
  client_id : ClientId,
  client_secret : ClientSecret,
  code : String,
  redirect_uri : RedirectUrl,
  pkce_verifier : PkceCodeVerifier,
) -> TokenRequest {
  {
    token_url,
    client_id,
    client_secret,
    code,
    redirect_uri,
    grant_type: "authorization_code",
    pkce_verifier: Some(pkce_verifier),
  }
}

///|
/// Build the request body for token request
/// Returns application/x-www-form-urlencoded format
pub fn TokenRequest::build_request_body(self : TokenRequest) -> String {
  let params : Map[String, String] = {}

  // grant_type (required)
  params["grant_type"] = self.grant_type

  // code (required)
  params["code"] = self.code

  // redirect_uri (required)
  params["redirect_uri"] = self.redirect_uri.to_string()

  // client_id (optional if using Basic auth, but we include it)
  params["client_id"] = self.client_id.to_string()

  // client_secret (optional if using Basic auth)
  params["client_secret"] = self.client_secret.to_string()

  // PKCE code_verifier (optional, for enhanced security)
  match self.pkce_verifier {
    Some(verifier) => params["code_verifier"] = verifier.to_string()
    None => ()
  }

  build_form_urlencoded_body(params)
}

///|
/// Get authorization header for Basic authentication
pub fn TokenRequest::get_auth_header(self : TokenRequest) -> String {
  build_basic_auth_header(self.client_id, self.client_secret)
}

///|
/// Execute the token request using HTTP client
/// Returns TokenResponse on success, OAuth2Error on failure
pub async fn TokenRequest::execute(
  self : TokenRequest,
  http_client : OAuth2HttpClient,
) -> Result[TokenResponse, OAuth2Error] {
  // Build request body
  let body = self.build_request_body()

  // Set up headers
  let headers : HttpHeaders = {}
  headers["Content-Type"] = "application/x-www-form-urlencoded"
  // Optional: Use Basic authentication
  // headers["Authorization"] = self.get_auth_header()

  // Send POST request
  let response = match
    http_client.post(self.token_url.to_string(), headers, body) {
    Ok(resp) => resp
    Err(err) => return Err(err)
  }

  // Check if response is successful (2xx status code)
  if response.is_error() {
    // Parse OAuth2 error from response body
    let error = parse_oauth2_error(response.body)
    return Err(error)
  }

  // Parse token response
  parse_token_response(response.body)
}

///|
/// Parse token response from JSON
/// Expected format: {"access_token":"...","token_type":"Bearer",...}
pub fn parse_token_response(
  json : String,
) -> Result[TokenResponse, OAuth2Error] {
  // Extract required fields
  let access_token_str = extract_json_string_value(json, "access_token")
  let token_type = extract_json_string_value(json, "token_type")

  match (access_token_str, token_type) {
    (Some(token), Some(type_)) => {
      // Extract optional fields
      let expires_in = extract_json_int_value(json, "expires_in")
      let refresh_token_str = extract_json_string_value(json, "refresh_token")
      let scope = extract_json_string_value(json, "scope")
      let id_token = extract_json_string_value(json, "id_token")

      // Create tokens
      let access_token = AccessToken::new(token)
      let refresh_token = match refresh_token_str {
        Some(rt) => Some(RefreshToken::new(rt))
        None => None
      }

      let response = TokenResponse::new(
        access_token, type_, expires_in, refresh_token, scope, id_token,
      )
      Ok(response)
    }
    _ => Err(ParseError("Missing required fields in token response: \{json}"))
  }
}

///|
/// Extract integer value from JSON-like text
fn extract_json_int_value(json : String, key : String) -> Int? {
  let parsed = @json.parse(json) catch { _ => return None }

  match parsed {
    Object(map) =>
      match map.get(key) {
        Some(Number(n, ..)) => Some(n.to_int())
        _ => None
      }
    _ => None
  }
}