#!/usr/bin/env nu
# Info: UpCloud 
# api.nu

export def upcloud_api_auth [
]: nothing -> string {
   let upcloud_auth = if (($env | get -o UPCLOUD_AUTH | default "") | is-empty) {
      let upcloud_username = ($env | get -o UPCLOUD_USERNAME | default "")
      let upcloud_password = ($env | get -o UPCLOUD_PASSWORD | default "")
      $"($upcloud_username):($upcloud_password)" | encode base64
   } else { 
      ($env | get -o UPCLOUD_AUTH | default "")
   }
   if $upcloud_auth == ":" or ($upcloud_auth | is-empty) { 
      _print $"🛑 Not found (_ansi purple)UpCloud(_ansi reset) (_ansi red)credentials(_ansi reset)" 
      return ""
   } 
   $upcloud_auth 
}

export def upcloud_api_url [
    url_path: string 
]: nothing -> any {
   let upcloud_api_url = ($env | get -o UPCLOUD_API_URL | default "")
   if ($upcloud_api_url | is-empty) {
      _print $"🛑 Not found (_ansi purple)UpCloud(_ansi reset) (_ansi red)API URL(_ansi reset) not found" 
      return ""
   }
   $"($upcloud_api_url)/($url_path)"
} 

export def upcloud_api_request [
   method: string
   url_path: string 
   data?: any
]: nothing -> any {
   let $upcloud_auth = (upcloud_api_auth)
   let upcloud_api_url = (upcloud_api_url $url_path)
   if ($upcloud_auth | is-empty) or ($upcloud_api_url | is-empty) { return "" }

   # http options $"($upcloud_api_url)/($url_path)" --allow-errors --headers [Origin "https://api.upcloud.com" Access-Control-Request-Headers "Content-Type, X-Custom-Header" Access-Control-Request-Method GET, "Authorization" $" Basic ($upcloud_auth)"]
   let result = match $method {
      "post" => {
         if ($data | describe | str starts-with "record") {
            http post --content-type application/json --allow-errors --headers ["Authorization" $" Basic ($upcloud_auth)"] $upcloud_api_url $data
         } else {
            http post --allow-errors --headers ["Authorization" $" Basic ($upcloud_auth)"] $upcloud_api_url $data
         }
      },
      "put" =>  { 
         http put --allow-errors --headers ["Authorization" $" Basic ($upcloud_auth)"] $upcloud_api_url $data
      }
      "delete" =>  { 
         http delete --allow-errors --headers ["Authorization" $" Basic ($upcloud_auth)"] $upcloud_api_url
      }
      _ =>  { 
         http get --allow-errors --headers ["Authorization" $" Basic ($upcloud_auth)"] $upcloud_api_url
      }
   } 
   if ($result | describe) == "string" { 
     if ($result | is-empty) { return "OK" } 
     _print $"🛑 Error (_ansi purple)UpCloud(_ansi reset) (_ansi red)($upcloud_api_url)(_ansi reset):\n ($result)" 
     return ""
   }
   let status = ($result | get -o status | default "") 
   let error = ($result | get -o error | default "") 
   if ($status | is-not-empty) or ($error | is-not-empty) { 
     _print $"🛑 Error (_ansi purple)UpCloud(_ansi reset) (_ansi red)($upcloud_api_url)(_ansi reset)\n ($status) ($error))" 
     return ""
   } 
   $result
}

export def upcloud_api_new_server [
   server: record 
]: nothing -> record {
   { 
      hostname: "dev-wrkr",
      zone: "es-mad1",
      title: "dev-wrkr Debian server",
      labels: {
         label: [
         {
            "key": "test",
            "value": ""
         }
         ]
      },
      plan: "DEV-1xCPU-1GB",
      #plan: "DEV-1xCPU-4GB",
      metadata: "yes",
      simple_backup: "0400,dailies",
      timezone: "UTC",
      storage_devices: {
         storage_device: [
         {
            action: "clone",
            labels: [
               {
               "key": "foo",
               "value": "bar"
               }
            ],
            storage: "01000000-0000-4000-8000-000020070100" #  Debian GNU/Linux 12 (Bookworm)
            encrypted: "no",
            title: "dev-wrkr Debian from a template",
            #size: 50,
            size: 20,
            tier: "standard"
            #tier: "maxiops"
         }
         ]
      },
      networking: {
         interfaces: {
         interface: [
            {
               ip_addresses: {
                  ip_address: [
                     {
                        family: "IPv4"
                     }
                  ]
               },
               type: "public"
            },
            {
               ip_addresses: {
                  ip_address: [
                     {
                        family: "IPv4"
                     }
                  ]
               },
               type: "utility"
            },
            {
               ip_addresses: {
                  ip_address: [
                     {
                        family: "IPv6"
                     }
                  ]
               },
               type: "public"
            },
            {
               type: "private",
               network: "03b1115c-522b-4608-ae08-9a4d32a2d16d"
               source_ip_filtering: "yes"
               ip_addresses: {
                  ip_address: [
                     {
                        family: "IPv4",
                        address: "10.11.2.11",
                        dhcp_provided: "no"
                     },
                  ]
               }
            }
         ]
         }
      },
      login_user: {
         ssh_keys: {
         ssh_key: [
            "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIM5GLeuDFUdLl7p72xt4nCOmCrdwP5QG1F16kIAQQlMT cdci"
         ]
         }
      }
   }
}

export def upcloud_api_list_servers [
   format?: string
]: nothing -> list {
   let result = (upcloud_api_request "get" "server" )
   if ($result | is-empty) {
      return []
   }
   match $format { 
      "main" => {
         mut servers_list = []
         for it in ($result | get -o servers | flatten) {
            let srv = ($it | get -o server )
            $servers_list = ($servers_list | append {
               state: ($srv| get -o state | default ""),
               hostname: ($srv| get -o hostname | default ""),
               uuid: ($srv| get -o uuid | default ""),
               title: ($srv| get -o title | default ""),
               plan: ($srv| get -o plan | default ""),
               zone: ($srv| get -o zone | default ""),
               memory_amount: ($srv| get -o memory_amount | default ""),
               core_number: ($srv| get -o core_number | default ""),
               simple_backup: ($srv| get -o simple_backup | default ""),
               server_group: ($srv| get -o server_group | default ""),
            })
         } 
         $servers_list
      },
      _ => ($result | get -o servers | flatten)
   }
}

export def upcloud_api_server_info [
   hostname: string
]: nothing ->  any {
   let servers_list = (upcloud_api_list_servers | where {|it| $it.server.hostname == $hostname })
   ($servers_list | get -o 0 | get -o server | default "")
}

export def upcloud_api_server_uuid [
   hostname: string
   uuid: string
]: nothing -> string {
    if ($uuid | is-empty) {
      if ($hostname | is-empty) { return "" }
      (upcloud_api_server_info $hostname | get -o uuid | default "")
   } else { $uuid }
}

export def upcloud_api_server_ip [
   server_info: record
   type: string = "public"
   family: string = "IPv4"
]: nothing -> any {
   ($server_info | get -o server | get -o networking | get -o interfaces | get -o interface
      | flatten | where {|item| $item.type == $type} | get -o ip_address 
      | flatten | where {|it| $it.family == $family} | get -o address | get -o 0 | default ""
   )
}
export def upcloud_api_server_uuid_ip [
   uuid: string
   type: string = "public"
   family: string = "IPv4"
]: nothing -> any {
   let result = (upcloud_api_request "get" $"server/($uuid)" )
   if ($result | is-empty) { return "" }
   (upcloud_api_server_ip $result $type $family)
}
export def upcloud_api_server_new_state [
   state: string
   uuid:  string
   wait: int = 60
]: nothing -> any { 
   (upcloud_api_request 
      "post" 
      $"server/($uuid)/($state)" 
      { stop_server: { stop_type: "soft", timeout: $wait } }
   )
}
export def upcloud_api_server_state [
   hostname: string
   uuid:  string
]: nothing -> any { 
   if ($uuid | is-not-empty) { 
      (upcloud_api_request "get" $"server/($uuid)" | get -o state | default "" )
   } else if ($hostname | is-not-empty) {
      (upcloud_api_server_info $hostname | get -o state | default "")
   } else {
      ""
   } 
}
export def upcloud_api_server_delete [
   hostname: string
   uuid: string
   # storages	Controls what to do with storages related to the deleted server.	0, 1, true, false	0
   storage: bool = true
   # backups	If storages are to be deleted, controls what to do with backups related to the storages.	keep, keep_latest, delete	keep
   backups: string = "delete"
]: nothing -> any {
   let server_uuid = (upcloud_api_server_uuid $hostname $uuid)
   if ($server_uuid | is-empty) {
      _print $"🛑 Error (_ansi purple)UpCloud(_ansi reset) DELETE (_ansi red)($hostname) ($uuid)(_ansi reset)" 
      return 
   }
   (upcloud_api_request "delete" $"server/($uuid)?storages=($storage)&backups=($backups)")
}
export def upcloud_api_get_info [ 
   hostname: string 
   type: string = "public"
   family: string = "IPv4"
]: nothing -> string {
   let server = (upcloud_api_server_info $hostname)
   if ($server | is-empty) { return "" } 
   # _print ($server | table -e)
   let uuid = ($server | get -o uuid | default "") 
   if ($uuid | is-empty) {  return "" }
   (upcloud_api_server_uuid_ip $uuid "public" "IPv4")
}
export def upcloud_api_test [
]: nothing -> string {
   let hostname = "dev-wrkr"

   # let result = (upcloud_api_request "get" "account")
   # if ($result | is-not-empty) and ($result | get -o account |  get -o credits | default "" | is-not-empty) {
   #    _print $"Account '($result | get -o account | get -o username | default "")' credit: ($result | get -o account | get -o credits)"
   # }

   let server_info = (upcloud_api_server_info $hostname)
   if ($server_info | is-not-empty) { 
      _print $"🛑 Error (_ansi purple)UpCloud(_ansi reset) create (_ansi red)($hostname)(_ansi reset)" 
      _print $"Server (_ansi green)($hostname)(_ansi reset) ($server_info | get -o uuid | default "") => ($server_info | get -o state | default "")"
   } else { 
      _print $"Server (_ansi green)($hostname)(_ansi reset) creation ..."
      let server_data = (upcloud_api_new_server {})
      let result = (upcloud_api_request "post" "server" { server: $server_data} )
      if ($result | is-not-empty) {
         let pub_ip = (upcloud_api_server_ip $result "public" "IPv4")
         if ($pub_ip | is-not-empty) { 
            _print $"ssh -i $HOME/.ssh/id_cdci -l root ($pub_ip)"
      }
      }
   }

   #let pub_ip = (upcloud_get_info $hostname "public" "IPv4")

   _print $"Server (_ansi green)($hostname)(_ansi reset) state: (upcloud_api_server_state $hostname "")"

   let servers_list = (upcloud_api_list_servers "main")
   _print ($servers_list | table -i false -e)
   let server = (upcloud_api_server_info $hostname) 
   if ($server | is-empty) { exit } 
   # _print ($server | table -e)
   let uuid = ($server | get -o uuid | default "") 
   if ($uuid | is-empty) {  exit }
   let pub_ip = (upcloud_api_server_uuid_ip $uuid "public" "IPv4")
   if ($pub_ip | is-not-empty) { 
      _print $"ssh -i $HOME/.ssh/id_cdci -l root ($pub_ip)"
   }

   let server_state = (upcloud_api_server_state $hostname "")
   if $server_state == "maintenance"  or $server_state == "error" {
      _print $"🛑 Server (_ansi green)($hostname)(_ansi reset) in (_ansi red)($server_state)(_ansi reset) !!! "
      exit 1
   }
   if $server_state == "started" {
      let wait = 20
      let max_wait = 240
      mut wait_time = 0
      _print $"Server (_ansi green)($hostname)(_ansi reset) state: (_ansi yellow)($server_state)(_ansi reset)"
      _print $"Server (_ansi green)($hostname | default "")(_ansi reset) ($uuid) to (_ansi yellow)stop(_ansi reset) state ... try every ($wait)sec until ($max_wait)sec"
      _print -n $"(_ansi blue_bold) 🌥 (_ansi reset)"
      (upcloud_api_server_new_state "stop" $uuid 30) 
      while true {
         if (upcloud_api_server_state $hostname "") == "stopped" { break }
         $wait_time = ($wait_time + $wait)
         if ($wait_time > $max_wait) {
            _print $"🛑 Server (_ansi green)($hostname)(_ansi reset) state (_ansi red)stop(_ansi reset) not found in ($max_wait)secs !!! "
            exit 1
         }
         print -n $"(_ansi blue_bold) 🌥 (_ansi reset) [($wait_time)]"
         sleep ($"($wait)sec"| into duration)
      } 
      _print ""
   }
   let result = (upcloud_api_server_delete "" $uuid)
   if $result == "OK" {
   _print $"Server (_ansi green)($hostname)(_ansi reset) DELETED "
   _print (upcloud_api_server_info $hostname)
   }
}