#!/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/config/accessor.nu *
use ../lib_provisioning/utils/interface.nu *
use ../lib_provisioning/utils/init.nu *
use ../lib_provisioning/utils/error.nu *
use ../lib_provisioning/utils/version.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
  --yes (-y)       # Auto-confirm prompts (skip interactive prompts)
] {
  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)
    exit
  }
  let tools_task = if $task == null { "" } else { $task }
  let tools_args = if ($args | length) == 0 { ["all"] } else { $args }
  let provisioning_path = ($env.PROVISIONING? | default (get-config-base-path))
  let core_cli = ($provisioning_path | path join "core" | path join "cli")
  match $tools_task {
    "install" => {
      let update_tools = if $update { "--update" } else { "" }
      _print $"(_ansi blue_bold)((get-provisioning-name))(_ansi reset) tools_install (_ansi green_bold)($tools_args | str join ' ') ($update_tools)(_ansi reset) "
      ^$"($core_cli)/tools-install" ...$tools_args $update_tools
    },
    "show" | "s" |  "info"  => {
      _print $"(_ansi blue_bold)((get-provisioning-name))(_ansi reset) tools (_ansi green_bold)($tools_args | str join ' ')(_ansi reset) "
      let target = ($args | get 0? | default "")
      let match = ($args | get 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)((get-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)((get-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)((get-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)((get-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 --auto-yes=$yes
      return
    },
    "pin" => {
      let component = ($args | get 0? | default null)
      if ($component | is-empty) {
        _print "❌ Please specify a component ID"
        return
      }
      _print $"(_ansi blue_bold)((get-provisioning-name))(_ansi reset) tools pin (_ansi green_bold)($component)(_ansi reset) "
      set-fixed $component true
      return
    },
    "unpin" => {
      let component = ($args | get 0? | default null)
      if ($component | is-empty) {
        _print "❌ Please specify a component ID"
        return
      }
      _print $"(_ansi blue_bold)((get-provisioning-name))(_ansi reset) tools unpin (_ansi green_bold)($component)(_ansi reset) "
      set-fixed $component false
      return
    },
    "taskserv-versions" | "tv" => {
      _print $"(_ansi blue_bold)((get-provisioning-name))(_ansi reset) taskserv versions (_ansi green_bold)($tools_args | str join ' ')(_ansi reset) "
      let format = ($args | get 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)((get-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 decl_file | table)
      return
    },
    "taskserv-update" | "tu" => {
      _print $"(_ansi blue_bold)((get-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)((get-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"  => {
      _print (provisioning_tools_options)
    },
    _ => {
      on_tools_task $core_cli $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
] {
  let req_versions = (get-provisioning-req-versions)
  if ($req_versions | is-empty) or (not ($req_versions | path exists)) {
    _print $"(_ansi yellow)Tools registry not available(_ansi reset) — set PROVISIONING_REQ_VERSIONS or paths.req_versions."
    return
  }
  let tools_data = (open $req_versions)
  if ($match | is-empty) {
    _print ($tools_data | table -e)
  } else {
    let data_to_show = if ($match in ($tools_data | columns)) { $tools_data | get $match } else { null }
    _print ($data_to_show | table -e)
  }
}
export def show_provs_info [
  match: string
] {
  if not ((get-providers-path)| path exists) {
    _print  $"❗Error providers path (_ansi red)((get-providers-path))(_ansi reset) not found"
    return
  }
  ^ls (get-providers-path) | each {|prv|
    if ($match | is-empty) or $match == ($prv | str trim) {
      let prv_path = ((get-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
] {
  if not ((get-provisioning-req-versions) | path exists) {
    _print  $"❗Error tools path (_ansi red)((get-provisioning-req-versions))(_ansi reset) not found"
    return
  }
  let tools_data = (open (get-provisioning-req-versions))
  let tool_name = if ($tools_task in ($tools_data | columns)) { $tools_data | get $tools_task } else { null }
  if ($tool_name | is-not-empty) {
    _print $"(_ansi blue_bold)((get-provisioning-name))(_ansi reset) tools check (_ansi green_bold)($tools_task)(_ansi reset) "
    ^$"($core_bin)/tools-install" check $tools_task
    exit
  }
}

# Tools help output - displayed by "provisioning tools help"
def provisioning_tools_options [] {
  (
    $"(_ansi yellow_bold)╔══════════════════════════════════════════════════╗(_ansi reset)\n" +
    $"(_ansi yellow_bold)║(_ansi reset)  🔧 TOOLS & DEPENDENCIES                      (_ansi yellow_bold)║(_ansi reset)\n" +
    $"(_ansi yellow_bold)╚══════════════════════════════════════════════════╝(_ansi reset)\n\n" +

    $"(_ansi green_bold)[Installation](_ansi reset) Tool Setup\n" +
    $"  (_ansi blue)tools install(_ansi reset)        - Install all tools\n" +
    $"  (_ansi blue)tools install <tool>(_ansi reset) - Install specific tool [aws|hcloud|upctl]\n" +
    $"  (_ansi blue)tools install --update(_ansi reset) - Force reinstall all tools\n\n" +

    $"(_ansi green_bold)[Version Management](_ansi reset) Tool Versions\n" +
    $"  (_ansi blue)tools check(_ansi reset)          - Check all tool versions\n" +
    $"  (_ansi blue)tools versions(_ansi reset)       - Show configured versions\n" +
    $"  (_ansi blue)tools check-updates(_ansi reset)  - Check for available updates\n" +
    $"  (_ansi blue)tools apply-updates(_ansi reset)  - Apply configuration updates [--dry-run]\n\n" +

    $"(_ansi green_bold)[Tool Information](_ansi reset) Tool Details\n" +
    $"  (_ansi blue)tools show(_ansi reset)           - Display tool information\n" +
    $"  (_ansi blue)tools show all(_ansi reset)       - Show all tools and providers\n" +
    $"  (_ansi blue)tools show <tool>(_ansi reset)    - Tool-specific information\n" +
    $"  (_ansi blue)tools show provider(_ansi reset)  - Show provider information\n\n" +

    $"(_ansi green_bold)[Pinning & Configuration](_ansi reset) Version Control\n" +
    $"  (_ansi blue)tools pin <tool>(_ansi reset)     - Pin tool to current version\n" +
    $"  (_ansi blue)tools unpin <tool>(_ansi reset)   - Unpin tool for auto-update\n\n" +

    $"(_ansi green_bold)[Provider Tools](_ansi reset) Cloud CLI Tools\n" +
    $"  (_ansi blue)tools check aws(_ansi reset)      - Check AWS CLI status\n" +
    $"  (_ansi blue)tools check hcloud(_ansi reset)   - Check Hetzner CLI status\n" +
    $"  (_ansi blue)tools check upctl(_ansi reset)    - Check UpCloud CLI status\n\n" +

    $"(_ansi green_bold)EXAMPLES(_ansi reset)\n\n" +

    $"  # Check all tool versions\n" +
    $"  provisioning tools check\n\n" +

    $"  # Check specific provider tool\n" +
    $"  provisioning tools check hcloud\n" +
    $"  provisioning tools versions\n\n" +

    $"  # Check for updates and apply\n" +
    $"  provisioning tools check-updates\n" +
    $"  provisioning tools apply-updates --dry-run\n" +
    $"  provisioning tools apply-updates\n\n" +

    $"  # Install or update tools\n" +
    $"  provisioning tools install\n" +
    $"  provisioning tools install --update\n" +
    $"  provisioning tools install hcloud\n\n" +

    $"  # Pin/unpin specific tools\n" +
    $"  provisioning tools pin upctl\n" +
    $"  provisioning tools unpin upctl\n\n" +

    $"(_ansi green_bold)SUPPORTED TOOLS(_ansi reset)\n\n" +

    $"  • (_ansi cyan)aws(_ansi reset)     - AWS CLI v2\n" +
    $"  • (_ansi cyan)hcloud(_ansi reset)  - Hetzner Cloud CLI\n" +
    $"  • (_ansi cyan)upctl(_ansi reset)   - UpCloud CLI\n" +
    $"  • (_ansi cyan)nickel(_ansi reset)     - Nickel configuration language\n" +
    $"  • (_ansi cyan)nu(_ansi reset)      - Nushell scripting engine\n\n" +

    $"(_ansi green_bold)VERSION INFORMATION(_ansi reset)\n\n" +

    $"  Each tool can have:\n" +
    $"    - Configured version: Target version in config\n" +
    $"    - Installed version:  Currently installed on system\n" +
    $"    - Latest version:     Available upstream\n" +
    $"    - Status:             not_installed, installed, update_available, or ahead\n\n" +

    $"(_ansi green_bold)TOOL STATUS MEANINGS(_ansi reset)\n\n" +

    $"  not_installed   - Tool not found on system, needs installation\n" +
    $"  installed       - Tool is installed and version matches config\n" +
    $"  update_available - Newer version available, can be updated\n" +
    $"  ahead           - Installed version is newer than configured\n" +
    $"  behind          - Installed version is older than configured\n\n" +

    $"(_ansi default_dimmed)💡 Use provisioning tools install to set up all required tools\n" +
    $"   Most tools are optional but recommended for specific cloud providers\n" +
    $"   Pinning ensures version stability for production deployments(_ansi reset)\n"
  )
}
