#!/usr/bin/env nu 
# Info: Script to run Provisioning
# Author: JesusPerezLorenzo 
# Release: 1.0.4
# Date: 30-4-2024

use std log
#use lib_provisioning *
use ../env.nu *
use ../lib_provisioning/utils/interface.nu *
use ../lib_provisioning/utils/init.nu *
use ../lib_provisioning/utils/error.nu *
use ../lib_provisioning/utils/version_manager.nu *
use ../lib_provisioning/utils/version_formatter.nu *
use ../lib_provisioning/utils/version_loader.nu *
use ../lib_provisioning/utils/version_registry.nu *
use ../lib_provisioning/utils/version_taskserv.nu *

# - > Tools  management
export def "main tools" [
  task?: string # tools tasks for tools command
  ...args       # tools options
  --update (-u) # Update tools
  --debug (-x)  # Use Debug mode
  --xm             # Debug with PROVISIONING_METADATA
  --xc             # Debuc for task and services locally PROVISIONING_DEBUG_CHECK
  --xr             # Debug for remote servers PROVISIONING_DEBUG_REMOTE
  --xld            # Log level with DEBUG PROVISIONING_LOG_LEVEL=debug
  --metadata       # Error with metadata (-xm)
  --notitles       # not tittles
  --helpinfo (-h)  # For more details use options "help" (no dashes)
  --out: string    # Print Output format: json, yaml, text (default)
  --dry-run (-n)   # Dry run mode for update operations
  --force (-f)     # Force updates even if fixed
]: nothing -> nothing {
  if ($out | is-not-empty) {
    $env.PROVISIONING_OUT = $out 
    $env.PROVISIONING_NO_TERMINAL = true
  }
  if (use_titles) { show_titles }
  if $helpinfo {
    _print (provisioning_tools_options)
    # if not $env.PROVISIONING_DEBUG { end_run "" } 
    exit
  }
  let tools_task = if $task == null { "" } else { $task }
  let tools_args = if ($args | length) == 0 { ["all"] } else { $args }
  let core_bin = ($env.PROVISIONING | path join "core" | path join "bin")
  match $tools_task {
    "install" => {
      let update_tools = if $update { "--update" } else { "" }
      _print $"(_ansi blue_bold)($env.PROVISIONING_NAME)(_ansi reset) tools_install (_ansi green_bold)($tools_args | str join ' ') ($update_tools)(_ansi reset) "
      ^$"($core_bin)/tools-install" ...$tools_args $update_tools
    },
    "show" | "s" |  "info"  => {
      _print $"(_ansi blue_bold)($env.PROVISIONING_NAME)(_ansi reset) tools (_ansi green_bold)($tools_args | str join ' ')(_ansi reset) "
      let target = ($args | get -o 0 | default "")
      let match = ($args | get -o 1 | default "")
      match $target { 
        "a" | "all" =>  { 
          (show_tools_info $target)
          (show_provs_info $match)
        },
        "p" | "prov" | "provider" | "providers" => (show_provs_info $match),
        _ => (show_tools_info $target),
      }
    },
    "" | "check" | "c"  => {
      _print $"(_ansi blue_bold)($env.PROVISIONING_NAME)(_ansi reset) tools check (_ansi green_bold)($tools_args | str join ' ')(_ansi reset) "

      # Get all results first
      let all_results = (check-versions --fetch-latest=false)
      let all_configs = (discover-configurations)

      # Filter based on arguments
      let filtered_results = if ($args | length) == 0 {
        # No args - show all
        $all_results
      } else if ($args | length) == 1 {
        let arg = ($args | get 0)
        # Handle special cases for providers
        if $arg == "providers" {
          # Show all provider components
          $all_results | where type == "provider"
        } else {
          # Check if it's a type or component ID
          let by_type = ($all_results | where type == $arg)
          let by_id = ($all_results | where id == $arg)
          if ($by_type | length) > 0 { $by_type } else { $by_id }
        }
      } else {
        # Multiple args - "provider upcloud" format
        let type_filter = ($args | get 0)
        let category_filter = ($args | get 1)

        if $type_filter == "provider" {
          # Filter by provider category
          let configs_by_category = ($all_configs | where type == "provider" and category == $category_filter)
          let ids_by_category = ($configs_by_category | get id)
          $all_results | where id in $ids_by_category
        } else {
          # Fallback to ID filtering
          let id_filter = $category_filter
          $all_results | where type == $type_filter and id == $id_filter
        }
      }

      _print ($filtered_results | select id type configured status | table)
    },
    "versions" | "v" => {
      _print $"(_ansi blue_bold)($env.PROVISIONING_NAME)(_ansi reset) tools versions (_ansi green_bold)($tools_args | str join ' ')(_ansi reset) "

      # Get all results first
      let all_results = (check-versions --fetch-latest=false)
      let all_configs = (discover-configurations)

      # Filter based on arguments (same logic as check command)
      let filtered_results = if ($args | length) == 0 {
        # No args - show all
        $all_results
      } else if ($args | length) == 1 {
        let arg = ($args | get 0)
        # Handle special cases for providers
        if $arg == "providers" {
          # Show all provider components
          $all_results | where type == "provider"
        } else {
          # Check if it's a type or component ID
          let by_type = ($all_results | where type == $arg)
          let by_id = ($all_results | where id == $arg)
          if ($by_type | length) > 0 { $by_type } else { $by_id }
        }
      } else {
        # Multiple args - "provider upcloud" format
        let type_filter = ($args | get 0)
        let category_filter = ($args | get 1)

        if $type_filter == "provider" {
          # Filter by provider category
          let configs_by_category = ($all_configs | where type == "provider" and category == $category_filter)
          let ids_by_category = ($configs_by_category | get id)
          $all_results | where id in $ids_by_category
        } else {
          # Fallback to ID filtering
          let id_filter = $category_filter
          $all_results | where type == $type_filter and id == $id_filter
        }
      }

      _print ($filtered_results | select id type configured status | table)
      _print "\nNote: Run 'tools check-updates' to fetch latest versions from remote sources"
      return
    },
    "check-updates" | "cu" => {
      _print $"(_ansi blue_bold)($env.PROVISIONING_NAME)(_ansi reset) tools check-updates (_ansi green_bold)($tools_args | str join ' ')(_ansi reset) "
      let types = if ($args | length) > 0 { $args } else { [] }
      check-available-updates --types=$types
      return
    },
    "apply-updates" | "au" => {
      _print $"(_ansi blue_bold)($env.PROVISIONING_NAME)(_ansi reset) tools apply-updates (_ansi green_bold)($tools_args | str join ' ')(_ansi reset) "
      let types = if ($args | length) > 0 { $args } else { [] }
      apply-config-updates --types=$types --dry-run=$dry_run --force=$force
      return
    },
    "pin" => {
      let component = ($args | get -o 0)
      if ($component | is-empty) {
        _print "❌ Please specify a component ID"
        return
      }
      _print $"(_ansi blue_bold)($env.PROVISIONING_NAME)(_ansi reset) tools pin (_ansi green_bold)($component)(_ansi reset) "
      set-fixed $component true
      return
    },
    "unpin" => {
      let component = ($args | get -o 0)
      if ($component | is-empty) {
        _print "❌ Please specify a component ID"
        return
      }
      _print $"(_ansi blue_bold)($env.PROVISIONING_NAME)(_ansi reset) tools unpin (_ansi green_bold)($component)(_ansi reset) "
      set-fixed $component false
      return
    },
    "taskserv-versions" | "tv" => {
      _print $"(_ansi blue_bold)($env.PROVISIONING_NAME)(_ansi reset) taskserv versions (_ansi green_bold)($tools_args | str join ' ')(_ansi reset) "
      let format = ($args | get -o 0 | default "table")
      let taskservs_path = if ($args | length) > 1 { ($args | get 1) } else { "" }
      show-version-status --taskservs-path=$taskservs_path --format=$format
      return
    },
    "taskserv-check" | "tc" => {
      _print $"(_ansi blue_bold)($env.PROVISIONING_NAME)(_ansi reset) taskserv check (_ansi green_bold)($tools_args | str join ' ')(_ansi reset) "
      let taskservs_path = if ($args | length) > 0 { ($args | get 0) } else { "" }
      let configs = (discover-taskserv-configurations --base-path=$taskservs_path)
      _print ($configs | select id version kcl_file | table)
      return
    },
    "taskserv-update" | "tu" => {
      _print $"(_ansi blue_bold)($env.PROVISIONING_NAME)(_ansi reset) taskserv update (_ansi green_bold)($tools_args | str join ' ')(_ansi reset) "
      let components = if ($args | length) > 0 { $args } else { [] }
      update-registry-versions --components=$components --dry-run=$dry_run
      return
    },
    "taskserv-sync" | "ts" => {
      _print $"(_ansi blue_bold)($env.PROVISIONING_NAME)(_ansi reset) taskserv sync (_ansi green_bold)($tools_args | str join ' ')(_ansi reset) "
      let taskservs_path = if ($args | length) > 0 { ($args | get 0) } else { "" }
      let component = if ($args | length) > 1 { ($args | get 1) } else { "" }
      taskserv-sync-versions --taskservs-path=$taskservs_path --component=$component --dry-run=$dry_run
      return
    },
    "help" | "helpinfo" | "h"  => {
      provisioning_tools_options
    },
    _ => {
      on_tools_task $core_bin $tools_task
      let text = $"expected to be one of [install, show, info, check, versions, check-updates, update-versions, pin, unpin], got ($tools_task)"
      (throw-error
        "🛑 invalid_option"
        $text
        #--span (metadata $pkg_dir | get span)
      )
    },
  }
  if not $env.PROVISIONING_DEBUG { end_run "" } 
}

export def show_tools_info [
  match: string
]: nothing -> nothing {
  let tools_data = (open  $env.PROVISIONING_REQ_VERSIONS)
  if ($match | is-empty) { 
    _print ($tools_data | table -e)
  } else { 
    _print ($tools_data | get -o $match | table -e)
  }
}
export def show_provs_info [
  match: string
]: nothing -> nothing {
  if not ($env.PROVISIONING_PROVIDERS_PATH| path exists) {
    _print  $"❗Error providers path (_ansi red)($env.PROVISIONING_PROVIDERS_PATH)(_ansi reset) not found"
    return 
  }
  ^ls $env.PROVISIONING_PROVIDERS_PATH | each {|prv| 
    if ($match | is-empty) or $match == ($prv | str trim) {
      let prv_path = ($env.PROVISIONING_PROVIDERS_PATH | path join ($prv | str trim) |  path join "provisioning.yaml")
      if ($prv_path | path exists) { 
        _print $"(_ansi magenta_bold)($prv | str trim | str upcase)(_ansi reset)"
        _print (open  $prv_path | table -e) 
      } 
    }
  }
}
export def on_tools_task [
  core_bin: string
  tools_task: string
]: nothing -> nothing {
  if not ($env.PROVISIONING_REQ_VERSIONS | path exists) { 
    _print  $"❗Error tools path (_ansi red)($env.PROVISIONING_REQ_VERSIONS)(_ansi reset) not found"
    return 
  }
  let tools_data = (open $env.PROVISIONING_REQ_VERSIONS)
  let tool_name = ($tools_data | get -o $tools_task)
  if ($tool_name | is-not-empty) {
    _print $"(_ansi blue_bold)($env.PROVISIONING_NAME)(_ansi reset) tools check (_ansi green_bold)($tools_task)(_ansi reset) "
    ^$"($core_bin)/tools-install" check $tools_task
    # if not $env.PROVISIONING_DEBUG { end_run "" } 
    exit
  }
}