///|
const COPILOT_GITHUB_CLIENT_ID = "Iv1.b507a08c87ecfe98"

///|
const COPILOT_GHES_CLIENT_ID = "Ov23li8tweQw6odWQebz"

///|
const COPILOT_DEFAULT_DOMAIN = "github.com"

///|
const COPILOT_CREDENTIALS_ARTIFACT = "auth/copilot-credentials.json"

///|
const COPILOT_EDITOR_VERSION = "vscode/1.110.1"

///|
const COPILOT_PLUGIN_VERSION = "copilot-chat/0.38.2"

///|
const COPILOT_USER_AGENT = "GitHubCopilotChat/0.38.2"

///|
const COPILOT_API_VERSION = "2025-10-01"

///|
const COPILOT_INTEGRATION_ID = "vscode-chat"

///|
priv suberror CopilotAuthError {
  CopilotAuthError(String)
} derive(Debug)

///|
priv struct CopilotCredentials {
  github_token : String
  copilot_token : String
  copilot_token_expires_at : Int64
  account_id : String
  login : String
  avatar_url : String?
  authenticated_at : Int64
  github_domain : String
} derive(ToJson, FromJson)

///|
priv struct CopilotDeviceCodeResponse {
  device_code : String
  user_code : String
  verification_uri : String
  expires_in : Int
  interval : Int
} derive(ToJson)

///|
fn copilot_credentials_path() -> String {
  @moonsuite.product_artifact_for_workspace_root(
    moongate_active_workspace_root(),
    "moongate",
    COPILOT_CREDENTIALS_ARTIFACT,
  )
}

///|
fn copilot_now_seconds() -> Int64 {
  @async.now() / 1000L
}

///|
fn copilot_json_string(obj : Map[String, Json], key : String) -> String? {
  match obj.get(key) {
    Some(String(value)) => Some(value)
    _ => None
  }
}

///|
fn copilot_json_int(obj : Map[String, Json], key : String) -> Int? {
  match obj.get(key) {
    Some(Number(value, ..)) => Some(value.to_int())
    Some(String(value)) =>
      try @string.parse_int(value, base=10) catch {
        _ => None
      } noraise {
        parsed => Some(parsed)
      }
    _ => None
  }
}

///|
fn copilot_json_int64(obj : Map[String, Json], key : String) -> Int64? {
  match obj.get(key) {
    Some(Number(value, ..)) => Some(value.to_int64())
    Some(String(value)) =>
      try @string.parse_int64(value, base=10) catch {
        _ => None
      } noraise {
        parsed => Some(parsed)
      }
    _ => None
  }
}

///|
fn copilot_json_bool(obj : Map[String, Json], key : String) -> Bool? {
  match obj.get(key) {
    Some(value) if value == Json::boolean(true) => Some(true)
    Some(value) if value == Json::boolean(false) => Some(false)
    Some(String("true")) => Some(true)
    Some(String("false")) => Some(false)
    _ => None
  }
}

///|
fn copilot_url_encode(s : String) -> String {
  let buf = StringBuilder::new()
  for c in s {
    match c {
      'A'..='Z' | 'a'..='z' | '0'..='9' | '-' | '_' | '.' | '~' =>
        buf.write_char(c)
      _ => {
        let bytes = @encoding/utf8.encode(c.to_string())
        for b in bytes {
          let hex = b.to_int()
          let high = (hex >> 4) & 0xF
          let low = hex & 0xF
          let hex_char = fn(n : Int) -> Char {
            if n < 10 {
              ('0'.to_int() + n).unsafe_to_char()
            } else {
              ('A'.to_int() + n - 10).unsafe_to_char()
            }
          }
          buf.write_char('%')
          buf.write_char(hex_char(high))
          buf.write_char(hex_char(low))
        }
      }
    }
  }
  buf.to_string()
}

///|
fn copilot_form_body(params : Array[(String, String)]) -> String {
  params
  .iter()
  .map(fn(param) {
    "\{copilot_url_encode(param.0)}=\{copilot_url_encode(param.1)}"
  })
  .collect()
  .join("&")
}

///|
fn copilot_normalize_domain(raw : String?) -> String {
  let value = raw.unwrap_or(COPILOT_DEFAULT_DOMAIN).trim()
  let no_protocol = if value.has_prefix("https://") {
    value[8:].to_owned()
  } else if value.has_prefix("http://") {
    value[7:].to_owned()
  } else {
    value.to_owned()
  }
  let mut end = no_protocol.length()
  for sep in ["/", "?", "#"] {
    match no_protocol.find(sep) {
      Some(index) if index < end => end = index
      _ => ()
    }
  }
  let host = no_protocol[:end].to_owned().trim().to_lower()
  if host.is_empty() || host.contains("@") {
    COPILOT_DEFAULT_DOMAIN
  } else {
    host.to_owned()
  }
}

///|
fn copilot_is_ghes(domain : String) -> Bool {
  domain != COPILOT_DEFAULT_DOMAIN
}

///|
fn copilot_github_client_id(domain : String) -> String {
  if copilot_is_ghes(domain) {
    COPILOT_GHES_CLIENT_ID
  } else {
    COPILOT_GITHUB_CLIENT_ID
  }
}

///|
fn copilot_github_api_base(domain : String) -> String {
  if copilot_is_ghes(domain) {
    "https://\{domain}/api/v3"
  } else {
    "https://api.github.com"
  }
}

///|
fn copilot_api_base(domain : String) -> String {
  if copilot_is_ghes(domain) {
    "https://copilot-api.\{domain}"
  } else {
    "https://api.githubcopilot.com"
  }
}

///|
fn copilot_account_id(domain : String, user_id : String) -> String {
  if copilot_is_ghes(domain) {
    "\{domain}:\{user_id}"
  } else {
    user_id
  }
}

///|
fn copilot_account_json(credentials : CopilotCredentials) -> Json {
  {
    "id": Json::string(credentials.account_id),
    "provider": Json::string("github_copilot"),
    "login": Json::string(credentials.login),
    "avatar_url": match credentials.avatar_url {
      Some(value) => Json::string(value)
      None => Json::null()
    },
    "authenticated_at": Json::number(credentials.authenticated_at.to_double()),
    "is_default": Json::boolean(true),
    "github_domain": Json::string(credentials.github_domain),
  }.to_json()
}

///|
fn copilot_auth_status_json(credentials : CopilotCredentials?) -> Json {
  let accounts = match credentials {
    Some(credentials) => [copilot_account_json(credentials)]
    None => []
  }
  {
    "provider": Json::string("github_copilot"),
    "accounts": Json::array(accounts),
    "default_account_id": match credentials {
      Some(credentials) => Json::string(credentials.account_id)
      None => Json::null()
    },
    "migration_error": Json::null(),
    "authenticated": Json::boolean(credentials is Some(_)),
    "username": match credentials {
      Some(credentials) => Json::string(credentials.login)
      None => Json::null()
    },
    "expires_at": match credentials {
      Some(credentials) =>
        Json::number(credentials.copilot_token_expires_at.to_double())
      None => Json::null()
    },
  }.to_json()
}

///|
async fn copilot_save_credentials(
  credentials : CopilotCredentials,
) -> Unit raise Error {
  let path = copilot_credentials_path()
  let dir = @pathx.dirname(path)
  @fsx.make_directory(dir, recursive=true, exists_ok=true)
  @fsx.write_to_file(path, credentials.to_json().stringify(), create=0o600)
}

///|
async fn copilot_load_credentials() -> CopilotCredentials raise Error {
  let path = copilot_credentials_path()
  if !@fsx.exists_as_file(path) {
    raise CopilotAuthError("Copilot credentials file not found")
  }
  @json.from_json(@json.parse(@fsx.read_file(path)))
}

///|
async fn copilot_try_load_credentials() -> CopilotCredentials? {
  try copilot_load_credentials() catch {
    _ => None
  } noraise {
    credentials => Some(credentials)
  }
}

///|
async fn copilot_delete_credentials() -> Unit {
  try {
    let path = copilot_credentials_path()
    if @fsx.exists_as_file(path) {
      @fsx.remove(path)
    }
  } catch {
    _ => ()
  }
}

///|
fn parse_copilot_device_code_response(
  json : Json,
) -> CopilotDeviceCodeResponse raise Error {
  guard json is Object(obj) else {
    raise CopilotAuthError("Device Code response is not an object")
  }
  guard copilot_json_string(obj, "device_code") is Some(device_code) else {
    raise CopilotAuthError("Device Code response missing device_code")
  }
  guard copilot_json_string(obj, "user_code") is Some(user_code) else {
    raise CopilotAuthError("Device Code response missing user_code")
  }
  guard copilot_json_string(obj, "verification_uri") is Some(verification_uri) else {
    raise CopilotAuthError("Device Code response missing verification_uri")
  }
  {
    device_code,
    user_code,
    verification_uri,
    expires_in: copilot_json_int(obj, "expires_in").unwrap_or(900),
    interval: copilot_json_int(obj, "interval").unwrap_or(5),
  }
}

///|
pub async fn copilot_start_device_flow(
  github_domain : String?,
) -> Result[Json, String] {
  let domain = copilot_normalize_domain(github_domain)
  let body = copilot_form_body([
    ("client_id", copilot_github_client_id(domain)),
    ("scope", "read:user"),
  ])
  try {
    let (response, data) = @http.post(
      "https://\{domain}/login/device/code",
      body,
      headers={
        "Accept": "application/json",
        "Content-Type": "application/x-www-form-urlencoded",
        "User-Agent": COPILOT_USER_AGENT,
      },
    )
    guard response.code is (200..=299) else {
      return Err(
        "GitHub device code request failed: \{response.code} - \{data.text()}",
      )
    }
    Ok(parse_copilot_device_code_response(data.json()).to_json())
  } catch {
    error => Err("\{error}")
  }
}

///|
fn copilot_pending_error(error : String?) -> Bool {
  error == Some("authorization_pending") || error == Some("slow_down")
}

///|
async fn copilot_poll_github_token(
  device_code : String,
  domain : String,
) -> Result[String?, String] {
  let body = copilot_form_body([
    ("client_id", copilot_github_client_id(domain)),
    ("device_code", device_code),
    ("grant_type", "urn:ietf:params:oauth:grant-type:device_code"),
  ])
  try {
    let (response, data) = @http.post(
      "https://\{domain}/login/oauth/access_token",
      body,
      headers={
        "Accept": "application/json",
        "Content-Type": "application/x-www-form-urlencoded",
        "User-Agent": COPILOT_USER_AGENT,
      },
    )
    guard response.code is (200..=299) else {
      return Err("GitHub token poll failed: \{response.code} - \{data.text()}")
    }
    guard data.json() is Object(obj) else {
      return Err("GitHub token response is not an object")
    }
    match copilot_json_string(obj, "access_token") {
      Some(token) => Ok(Some(token))
      None =>
        if copilot_pending_error(copilot_json_string(obj, "error")) {
          Ok(None)
        } else {
          Err(
            copilot_json_string(obj, "error_description").unwrap_or(
              copilot_json_string(obj, "error").unwrap_or(
                "GitHub token response missing access_token",
              ),
            ),
          )
        }
    }
  } catch {
    error => Err("\{error}")
  }
}

///|
async fn copilot_fetch_user(
  github_token : String,
  domain : String,
) -> Result[(String, String, String?), String] {
  try {
    let (response, data) = @http.get(
      "\{copilot_github_api_base(domain)}/user",
      headers={
        "Authorization": "Bearer \{github_token}",
        "Accept": "application/json",
        "User-Agent": COPILOT_USER_AGENT,
      },
    )
    guard response.code is (200..=299) else {
      return Err(
        "GitHub user request failed: \{response.code} - \{data.text()}",
      )
    }
    guard data.json() is Object(obj) else {
      return Err("GitHub user response is not an object")
    }
    let id = match obj.get("id") {
      Some(Number(value, ..)) => value.to_int64().to_string()
      Some(String(value)) => value
      _ => return Err("GitHub user response missing id")
    }
    guard copilot_json_string(obj, "login") is Some(login) else {
      return Err("GitHub user response missing login")
    }
    Ok((id, login, copilot_json_string(obj, "avatar_url")))
  } catch {
    error => Err("\{error}")
  }
}

///|
async fn copilot_fetch_copilot_token(
  github_token : String,
  domain : String,
) -> Result[(String, Int64), String] {
  if copilot_is_ghes(domain) {
    return Ok((github_token, copilot_now_seconds() + 3600L))
  }
  try {
    let (response, data) = @http.get(
      "\{copilot_github_api_base(domain)}/copilot_internal/v2/token",
      headers={
        "Authorization": "Bearer \{github_token}",
        "Accept": "application/json",
        "User-Agent": COPILOT_USER_AGENT,
        "Editor-Version": COPILOT_EDITOR_VERSION,
        "Editor-Plugin-Version": COPILOT_PLUGIN_VERSION,
        "Copilot-Integration-Id": COPILOT_INTEGRATION_ID,
      },
    )
    guard response.code is (200..=299) else {
      return Err(
        "Copilot token request failed: \{response.code} - \{data.text()}",
      )
    }
    guard data.json() is Object(obj) else {
      return Err("Copilot token response is not an object")
    }
    guard copilot_json_string(obj, "token") is Some(token) else {
      return Err("Copilot token response missing token")
    }
    guard copilot_json_int64(obj, "expires_at") is Some(expires_at) else {
      return Err("Copilot token response missing expires_at")
    }
    Ok((token, expires_at))
  } catch {
    error => Err("\{error}")
  }
}

///|
pub async fn copilot_poll_for_account(
  device_code : String,
  github_domain : String?,
) -> Result[Json?, String] {
  let domain = copilot_normalize_domain(github_domain)
  match copilot_poll_github_token(device_code, domain) {
    Ok(Some(github_token)) => {
      let (user_id, login, avatar_url) = match
        copilot_fetch_user(github_token, domain) {
        Ok(user) => user
        Err(error) => return Err(error)
      }
      let (copilot_token, expires_at) = match
        copilot_fetch_copilot_token(github_token, domain) {
        Ok(token) => token
        Err(error) => return Err(error)
      }
      let credentials : CopilotCredentials = {
        github_token,
        copilot_token,
        copilot_token_expires_at: expires_at,
        account_id: copilot_account_id(domain, user_id),
        login,
        avatar_url,
        authenticated_at: copilot_now_seconds(),
        github_domain: domain,
      }
      copilot_save_credentials(credentials) catch {
        error => return Err("\{error}")
      }
      Ok(Some(copilot_account_json(credentials)))
    }
    Ok(None) => Ok(None)
    Err(error) => Err(error)
  }
}

///|
pub async fn copilot_list_accounts() -> Json {
  match copilot_try_load_credentials() {
    Some(credentials) => Json::array([copilot_account_json(credentials)])
    None => Json::array([])
  }
}

///|
pub async fn copilot_get_auth_status() -> Json {
  copilot_auth_status_json(copilot_try_load_credentials())
}

///|
pub async fn copilot_is_authenticated() -> Bool {
  copilot_try_load_credentials() is Some(_)
}

///|
pub async fn copilot_logout() -> Json {
  copilot_delete_credentials()
  Json::null()
}

///|
pub async fn copilot_remove_account(
  account_id : String,
) -> Result[Json, String] {
  match copilot_try_load_credentials() {
    Some(credentials) if credentials.account_id == account_id => {
      copilot_delete_credentials()
      Ok(Json::null())
    }
    _ => Err("Account not found: \{account_id}")
  }
}

///|
pub async fn copilot_set_default_account(
  account_id : String,
) -> Result[Json, String] {
  match copilot_try_load_credentials() {
    Some(credentials) if credentials.account_id == account_id =>
      Ok(Json::null())
    _ => Err("Account not found: \{account_id}")
  }
}

///|
async fn copilot_valid_credentials(
  account_id : String?,
) -> Result[CopilotCredentials, String] {
  guard copilot_try_load_credentials() is Some(credentials) else {
    return Err("GitHub Copilot is not authenticated")
  }
  match account_id {
    Some(id) if id != credentials.account_id =>
      return Err("Account not found: \{id}")
    _ => ()
  }
  if copilot_is_ghes(credentials.github_domain) ||
    credentials.copilot_token_expires_at - 60L > copilot_now_seconds() {
    return Ok(credentials)
  }
  match
    copilot_fetch_copilot_token(
      credentials.github_token,
      credentials.github_domain,
    ) {
    Ok((token, expires_at)) => {
      let refreshed = {
        ..credentials,
        copilot_token: token,
        copilot_token_expires_at: expires_at,
      }
      copilot_save_credentials(refreshed) catch {
        error => return Err("\{error}")
      }
      Ok(refreshed)
    }
    Err(error) => Err(error)
  }
}

///|
pub async fn copilot_get_token(account_id : String?) -> Result[Json, String] {
  match copilot_valid_credentials(account_id) {
    Ok(credentials) => Ok(Json::string(credentials.copilot_token))
    Err(error) => Err(error)
  }
}

///|
fn copilot_headers(credentials : CopilotCredentials) -> Map[String, String] {
  {
    "Authorization": "Bearer \{credentials.copilot_token}",
    "Content-Type": "application/json",
    "copilot-integration-id": COPILOT_INTEGRATION_ID,
    "editor-version": COPILOT_EDITOR_VERSION,
    "editor-plugin-version": COPILOT_PLUGIN_VERSION,
    "user-agent": COPILOT_USER_AGENT,
    "x-github-api-version": COPILOT_API_VERSION,
  }
}

///|
pub async fn copilot_get_models(account_id : String?) -> Result[Json, String] {
  let credentials = match copilot_valid_credentials(account_id) {
    Ok(credentials) => credentials
    Err(error) => return Err(error)
  }
  try {
    let (response, data) = @http.get(
      "\{copilot_api_base(credentials.github_domain)}/models",
      headers=copilot_headers(credentials),
    )
    guard response.code is (200..=299) else {
      return Err(
        "Copilot models request failed: \{response.code} - \{data.text()}",
      )
    }
    let parsed = data.json()
    guard parsed is Object(obj) else { return Ok(parsed) }
    guard obj.get("data") is Some(Array(items)) else { return Ok(parsed) }
    let models : Array[Json] = []
    for item in items {
      guard item is Object(model_obj) else { continue }
      let enabled = copilot_json_bool(model_obj, "model_picker_enabled").unwrap_or(
        true,
      )
      if enabled {
        models.push(
          {
            "id": Json::string(
              copilot_json_string(model_obj, "id").unwrap_or(""),
            ),
            "name": Json::string(
              copilot_json_string(model_obj, "name").unwrap_or(""),
            ),
            "vendor": Json::string(
              copilot_json_string(model_obj, "vendor").unwrap_or(""),
            ),
            "model_picker_enabled": Json::boolean(enabled),
          }.to_json(),
        )
      }
    }
    Ok(Json::array(models))
  } catch {
    error => Err("\{error}")
  }
}

///|
pub async fn copilot_get_usage(account_id : String?) -> Result[Json, String] {
  let credentials = match copilot_valid_credentials(account_id) {
    Ok(credentials) => credentials
    Err(error) => return Err(error)
  }
  try {
    let (response, data) = @http.get(
      "\{copilot_github_api_base(credentials.github_domain)}/copilot_internal/user",
      headers={
        "Authorization": "token \{credentials.github_token}",
        "Content-Type": "application/json",
        "editor-version": COPILOT_EDITOR_VERSION,
        "editor-plugin-version": COPILOT_PLUGIN_VERSION,
        "user-agent": COPILOT_USER_AGENT,
        "x-github-api-version": COPILOT_API_VERSION,
      },
    )
    guard response.code is (200..=299) else {
      return Err(
        "Copilot usage request failed: \{response.code} - \{data.text()}",
      )
    }
    Ok(data.json())
  } catch {
    error => Err("\{error}")
  }
}