///|
/// metadata_discovery.mbt — OAuth metadata discovery for MCP server login.
///
/// RFC 9728 protected-resource metadata points at authorization servers;
/// RFC 8414 authorization-server metadata carries the endpoint URLs the
/// device/registration/refresh flows need. Both are GETs over the injected
/// transport; callers pass the complete well-known URL.

///|
pub(all) struct ResourceMetadata {
  authorization_servers : Array[String]
}

///|
pub fn ResourceMetadata::ResourceMetadata(
  authorization_servers~ : Array[String],
) -> ResourceMetadata {
  { authorization_servers, }
}

///|
pub async fn fetch_resource_metadata(
  uri : String,
  transport : &OAuthHttpTransport,
) -> ResourceMetadata raise OAuthError {
  let headers : Map[String, String] = Map([("Accept", "application/json")])
  let response = transport.get(uri, headers)
  if response.status < 200 || response.status >= 300 {
    raise OAuthError::HttpError(
      "resource metadata HTTP status=" +
      response.status.to_string() +
      ", body_chars=" +
      response.body.length().to_string(),
    )
  }
  let json = @json.parse(response.body) catch {
    err =>
      raise OAuthError::ParseError(
        "resource metadata response: " + err.to_string(),
      )
  }
  match json {
    Json::Object(map) =>
      match map.get("authorization_servers") {
        Some(Json::Array(items)) => {
          let servers : Array[String] = []
          for item in items {
            match item {
              Json::String(server) =>
                if server.length() > 0 {
                  servers.push(server)
                } else {
                  raise OAuthError::ParseError(
                    "empty entry in authorization_servers",
                  )
                }
              _ =>
                raise OAuthError::ParseError(
                  "non-string entry in authorization_servers",
                )
            }
          }
          if servers.length() == 0 {
            raise OAuthError::ParseError(
              "empty authorization_servers in resource metadata",
            )
          }
          ResourceMetadata::ResourceMetadata(authorization_servers=servers)
        }
        _ =>
          raise OAuthError::ParseError(
            "missing authorization_servers array in resource metadata",
          )
      }
    _ =>
      raise OAuthError::ParseError(
        "resource metadata response is not an object",
      )
  }
}

///|
pub(all) struct ServerMetadata {
  authorization_endpoint : String
  token_endpoint : String
  device_authorization_endpoint : String?
  registration_endpoint : String?
  scopes_supported : Array[String]
}

///|
pub fn ServerMetadata::ServerMetadata(
  authorization_endpoint~ : String,
  token_endpoint~ : String,
  device_authorization_endpoint? : String,
  registration_endpoint? : String,
  scopes_supported? : Array[String] = [],
) -> ServerMetadata {
  {
    authorization_endpoint,
    token_endpoint,
    device_authorization_endpoint,
    registration_endpoint,
    scopes_supported,
  }
}

///|
pub async fn fetch_server_metadata(
  metadata_url : String,
  transport : &OAuthHttpTransport,
) -> ServerMetadata raise OAuthError {
  let headers : Map[String, String] = Map([("Accept", "application/json")])
  let response = transport.get(metadata_url, headers)
  if response.status < 200 || response.status >= 300 {
    raise OAuthError::HttpError(
      "server metadata HTTP status=" +
      response.status.to_string() +
      ", body_chars=" +
      response.body.length().to_string(),
    )
  }
  let json = @json.parse(response.body) catch {
    err =>
      raise OAuthError::ParseError(
        "server metadata response: " + err.to_string(),
      )
  }
  match json {
    Json::Object(map) => {
      let authorization_endpoint = match map.get("authorization_endpoint") {
        Some(Json::String(s)) if s.length() > 0 => s
        _ =>
          raise OAuthError::ParseError(
            "missing authorization_endpoint in server metadata",
          )
      }
      let token_endpoint = match map.get("token_endpoint") {
        Some(Json::String(s)) if s.length() > 0 => s
        _ =>
          raise OAuthError::ParseError(
            "missing token_endpoint in server metadata",
          )
      }
      // Optional endpoint fields: present-and-non-empty wins, anything else
      // (missing, empty, wrong type) degrades to None.
      let device_authorization_endpoint : String? = match
        map.get("device_authorization_endpoint") {
        Some(Json::String(s)) if s.length() > 0 => Some(s)
        _ => None
      }
      let registration_endpoint : String? = match
        map.get("registration_endpoint") {
        Some(Json::String(s)) if s.length() > 0 => Some(s)
        _ => None
      }
      let scopes_supported : Array[String] = match map.get("scopes_supported") {
        Some(Json::Array(items)) => {
          let scopes : Array[String] = []
          for item in items {
            match item {
              Json::String(scope) => scopes.push(scope)
              _ => ()
            }
          }
          scopes
        }
        _ => []
      }
      ServerMetadata::{
        authorization_endpoint,
        token_endpoint,
        device_authorization_endpoint,
        registration_endpoint,
        scopes_supported,
      }
    }
    _ =>
      raise OAuthError::ParseError("server metadata response is not an object")
  }
}