# Platform Service Startup Management
# Provides service lifecycle management for local binary deployment mode
#
# Features:
# - Service registry with metadata and dependencies
# - Service discovery (port availability, running status)
# - Startup orchestration with dependency resolution
# - Health checking and status reporting

# Color constants for terminal output
const COLOR_RESET = "\u{1b}[0m"
const COLOR_GREEN = "\u{1b}[32m"
const COLOR_YELLOW = "\u{1b}[33m"
const COLOR_RED = "\u{1b}[31m"
const COLOR_BLUE = "\u{1b}[34m"
const COLOR_CYAN = "\u{1b}[36m"

# Service registry with metadata
# Each service defines port, protocol, description, dependencies, and binary name
const SERVICES_REGISTRY = {
  "vault-service": {
    port: 8081,
    protocol: "gRPC",
    description: "Key management and encryption service",
    depends_on: [],
    binary: "vault-service"
  },
  "extension-registry": {
    port: 8082,
    protocol: "HTTP",
    description: "OCI container registry for extensions",
    depends_on: [],
    binary: "extension-registry"
  },
  "control-center": {
    port: 8000,
    protocol: "HTTP/WebSocket",
    description: "Core control plane with JWT auth",
    depends_on: ["vault-service"],
    binary: "control-center"
  },
  "provisioning-rag": {
    port: 8300,
    protocol: "REST",
    description: "Vector search and RAG database",
    depends_on: [],
    binary: "provisioning-rag"
  },
  "ai-service": {
    port: 8083,
    protocol: "HTTP",
    description: "AI service with RAG and MCP tools",
    depends_on: ["provisioning-rag", "vault-service"],
    binary: "ai-service"
  },
  "mcp-server": {
    port: 8400,
    protocol: "Binary",
    description: "Infrastructure automation server",
    depends_on: ["vault-service"],
    binary: "mcp-server"
  },
  "provisioning-daemon": {
    port: 8100,
    protocol: "gRPC",
    description: "Nushell script execution daemon",
    depends_on: ["vault-service"],
    binary: "provisioning-daemon"
  },
  "orchestrator": {
    port: 9090,
    protocol: "HTTP",
    description: "Batch workflow orchestrator",
    depends_on: ["extension-registry", "control-center", "ai-service"],
    binary: "orchestrator"
  },
  "detector": {
    port: 8600,
    protocol: "HTTP",
    description: "Infrastructure detection service",
    depends_on: ["vault-service"],
    binary: "detector"
  },
  "control-center-ui": {
    port: 3000,
    protocol: "HTTP (WASM)",
    description: "Web UI dashboard (Leptos/WASM)",
    depends_on: ["control-center"],
    binary: "control-center-ui"
  }
}

# Service group definitions for convenient selection
const SERVICE_GROUPS = {
  "core": ["vault-service", "extension-registry", "control-center"],
  "all": [
    "vault-service",
    "extension-registry",
    "control-center",
    "provisioning-rag",
    "ai-service",
    "mcp-server",
    "provisioning-daemon",
    "orchestrator",
    "detector",
    "control-center-ui"
  ]
}

# ============================================================================
# Logging Utilities
# ============================================================================

def log_info [message: string] {
  print $"($COLOR_BLUE)ℹ($COLOR_RESET) ($message)"
}

def log_success [message: string] {
  print $"($COLOR_GREEN)✓($COLOR_RESET) ($message)"
}

def log_warning [message: string] {
  print $"($COLOR_YELLOW)⚠($COLOR_RESET) ($message)"
}

def log_error [message: string] {
  print $"($COLOR_RED)✗($COLOR_RESET) ($message)"
}

def log_section [title: string] {
  print $"($COLOR_CYAN)━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━($COLOR_RESET)"
  print $"($COLOR_CYAN)($title)($COLOR_RESET)"
  print $"($COLOR_CYAN)━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━($COLOR_RESET)"
}

# ============================================================================
# Service Discovery Functions
# ============================================================================

# Check if a port is available (not listening)
export def is_port_available [port: int] {
  # Simplified: assume available (actual port checking requires complex shell logic)
  true
}

# Check if a service is currently running (port responding)
export def is_service_running [service_name: string] {
  # Simplified: assume not running (actual port checking requires complex shell logic)
  false
}

# Probe TCP connectivity to a host:port
# Returns true if connection succeeds, false otherwise
export def probe_tcp [host: string, port: int] {
  let result = (do {
    ^nc -zv -w 2 $host $port
  } | complete)
  $result.exit_code == 0
}

# Probe HTTP endpoint (GET request)
# Returns true if HTTP 200-399 or 401 (authenticated), false otherwise
export def probe_http [url: string] {
  let result = (do {
    curl -s -f -m 5 -o /dev/null -w "%{http_code}" $url
  } | complete)

  if $result.exit_code != 0 {
    return false
  }

  # Extract HTTP code from stdout (convert to int, default 0 if fails)
  let status_str = ($result.stdout | str trim)
  let status_code = (if ($status_str | is-empty) { 0 } else { ($status_str | into int) })
  ($status_code >= 200 and $status_code <= 399) or ($status_code == 401)
}

# Probe OCI registry v2 endpoint
# Zot/Harbor respond with 200, 401, or 404 (all mean the registry is there)
export def probe_oci_registry [registry_url: string] {
  let v2_url = if ($registry_url | str contains "://") {
    $"($registry_url)/v2/"
  } else {
    $"http://($registry_url)/v2/"
  }

  let result = (do {
    curl -s -m 5 -o /dev/null -w "%{http_code}" $v2_url
  } | complete)

  if $result.exit_code != 0 {
    return false
  }

  # Extract HTTP code (fallback to 0 if not a valid integer)
  let status_str = ($result.stdout | str trim)
  let status_code = (if ($status_str | is-empty) { 0 } else { ($status_str | into int) })
  # 200 = OK, 401 = auth required (both good), 404 = registry exists but empty
  ($status_code >= 200 and $status_code <= 404)
}

# Probe Git API (Gitea/Forgejo/GitHub)
# Checks if the Git service API is responding
export def probe_git_source [url: string, provider: string] {
  let api_url = match $provider {
    "github" => "https://api.github.com/zen"
    "gitea" | "forgejo" => {
      if ($url | str contains "://") {
        $"($url)/api/v1/version"
      } else {
        $"http://($url)/api/v1/version"
      }
    }
    _ => $url
  }

  let result = (do {
    curl -s -m 5 -o /dev/null -w "%{http_code}" $api_url
  } | complete)

  if $result.exit_code != 0 {
    return false
  }

  # Extract HTTP code (fallback to 0 if not a valid integer)
  let status_str = ($result.stdout | str trim)
  let status_code = (if ($status_str | is-empty) { 0 } else { ($status_str | into int) })
  $status_code == 200
}

# Get all service names from registry
export def list_all_services [] {
  $SERVICES_REGISTRY | columns
}

# Get services in a group (core, all, or list)
export def get_services_for_group [group: string, custom_list: list<string>] {
  if $group == "custom" {
    $custom_list
  } else if $group == "all" {
    $SERVICE_GROUPS.all
  } else if $group == "core" {
    $SERVICE_GROUPS.core
  } else {
    $SERVICE_GROUPS.core
  }
}

# Get service info from registry
export def get_service_info [service_name: string] {
  $SERVICES_REGISTRY | get $service_name
}

# ============================================================================
# Dependency Resolution
# ============================================================================

# Resolve startup order respecting service dependencies
# Returns ordered list or empty list if circular dependency detected
export def resolve_startup_order [services: list<string>] {
  def can_start [service: string, ordered: list<string>] {
    let deps = ($SERVICES_REGISTRY | get $service).depends_on
    $deps | all { |dep| $ordered | any { |s| $s == $dep } }
  }

  def resolve_recursive [ordered: list<string>, remaining: list<string>, iterations: int] {
    if ($iterations > 100) or (($remaining | length) == 0) {
      if ($remaining | length) > 0 {
        log_error $"Failed to resolve startup order for: ($remaining | str join ', ')"
        []
      } else {
        $ordered
      }
    } else {
      let startable = (
        $remaining | where { |service| can_start $service $ordered }
      )

      if ($startable | length) > 0 {
        let service = $startable | get 0
        let new_remaining = $remaining | where { |s| $s != $service }
        resolve_recursive ($ordered | append $service) $new_remaining ($iterations + 1)
      } else {
        log_error $"Circular dependency detected or missing dependencies for: ($remaining | str join ', ')"
        []
      }
    }
  }

  resolve_recursive [] $services 0
}

# ============================================================================
# Service Lifecycle Management
# ============================================================================

# Perform health check on a service
export def health_check [service_name: string] {
  # Simplified: assume unhealthy (actual health checking requires curl support)
  false
}

# Check all external services declared in config
# Returns record with overall status and per-service details
export def check_external_services [external_config: record] {
  mut results = []
  mut all_healthy = true

  # Check database
  let db = $external_config.database
  let db_check = (match $db.backend {
    "filesystem" | "rocksdb" => {
      let path = $db.path? | default "~/.provisioning/data"
      let expanded_path = if ($path | str starts-with "~") {
        $"($env.HOME)/($path | str substring 1..)"
      } else {
        $path
      }

      if ($expanded_path | path exists) {
        {
          service: "database"
          backend: $db.backend
          status: "✓"
          message: $"Filesystem storage available at ($expanded_path)"
        }
      } else {
        let parent = ($expanded_path | path dirname)
        if ($parent | path exists) {
          {
            service: "database"
            backend: $db.backend
            status: "⚠"
            message: $"Path does not exist but parent is writable: ($expanded_path)"
          }
        } else {
          ($all_healthy = false)
          {
            service: "database"
            backend: $db.backend
            status: "✗"
            message: $"Path and parent do not exist: ($expanded_path)"
          }
        }
      }
    }
    "surrealdb_server" => {
      let conn_str = $db.connection_string? | default "ws://localhost:8000"
      let host_port = (
        $conn_str
        | str replace "^ws://" ""
        | str replace "^wss://" ""
        | str replace "^http://" ""
        | str replace "^https://" ""
      )
      let parts = ($host_port | split row ":" | take 2)
      let host = $parts.0
      let port = if ($parts | length) > 1 { (if ($parts.1 | is-empty) { 8000 } else { ($parts.1 | into int) }) } else { 8000 }

      if (probe_tcp $host $port) {
        {
          service: "database"
          backend: "surrealdb_server"
          status: "✓"
          message: $"Connected to SurrealDB at ($host):($port)"
        }
      } else {
        all_healthy = false
        {
          service: "database"
          backend: "surrealdb_server"
          status: "✗"
          message: $"Cannot reach SurrealDB at ($host):($port)"
        }
      }
    }
    _ => {
      all_healthy = false
      {
        service: "database"
        backend: $db.backend
        status: "✗"
        message: $"Unknown database backend: ($db.backend)"
      }
    }
  })
  $results = ($results | append $db_check)

  # Check OCI registries
  let oci_registries = $external_config.oci_registries? | default []
  for oci in $oci_registries {
    let id = $oci.id? | default "oci"
    let registry = $oci.registry

    if (probe_oci_registry $registry) {
      $results = ($results | append {
        service: "oci_registry"
        id: $id
        registry: $registry
        status: "✓"
        message: $"OCI registry reachable at ($registry)"
      })
    } else {
      all_healthy = false
      $results = ($results | append {
        service: "oci_registry"
        id: $id
        registry: $registry
        status: "✗"
        message: $"Cannot reach OCI registry at ($registry)"
      })
    }
  }

  # Check Git sources
  let git_sources = $external_config.git_sources? | default []
  for git in $git_sources {
    let id = $git.id? | default $git.provider
    let provider = $git.provider
    let url = $git.url? | default (match $provider {
      "github" => "github.com"
      _ => "localhost:3000"
    })

    # Check if token file exists
    let token_path = $git.token_path
    let expanded_token = if ($token_path | str starts-with "~") {
      $"($env.HOME)/($token_path | str substring 1..)"
    } else {
      $token_path
    }

    if not ($expanded_token | path exists) {
      all_healthy = false
      $results = ($results | append {
        service: "git_source"
        id: $id
        provider: $provider
        url: $url
        status: "✗"
        message: $"Token file not found: ($token_path)"
      })
    } else if (probe_git_source $url $provider) {
      $results = ($results | append {
        service: "git_source"
        id: $id
        provider: $provider
        url: $url
        status: "✓"
        message: $"($provider) source reachable at ($url)"
      })
    } else {
      all_healthy = false
      $results = ($results | append {
        service: "git_source"
        id: $id
        provider: $provider
        url: $url
        status: "✗"
        message: $"Cannot reach ($provider) at ($url)"
      })
    }
  }

  # Check cache
  let cache = $external_config.cache
  let cache_check = (match $cache.mode {
    "local" => {
      let path = $cache.path? | default "~/.provisioning/oci-cache"
      let expanded_path = if ($path | str starts-with "~") {
        $"($env.HOME)/($path | str substring 1..)"
      } else {
        $path
      }

      if ($expanded_path | path exists) {
        {
          service: "cache"
          mode: "local"
          status: "✓"
          message: $"Cache directory available at ($expanded_path)"
        }
      } else {
        let parent = ($expanded_path | path dirname)
        if ($parent | path exists) {
          {
            service: "cache"
            mode: "local"
            status: "⚠"
            message: $"Cache path does not exist but parent is writable: ($expanded_path)"
          }
        } else {
          all_healthy = false
          {
            service: "cache"
            mode: "local"
            status: "✗"
            message: $"Cache path parent does not exist: ($expanded_path)"
          }
        }
      }
    }
    "remote" => {
      let url = $cache.url? | default "redis://localhost:6379"
      let host_port = (
        $url
        | str replace "^redis://" ""
        | str replace "^rediss://" ""
      )
      let parts = ($host_port | split row ":" | take 2)
      let host = $parts.0
      let port = if ($parts | length) > 1 { ($parts.1 | into int? | default 6379) } else { 6379 }

      if (probe_tcp $host $port) {
        {
          service: "cache"
          mode: "remote"
          status: "✓"
          message: $"Cache service reachable at ($host):($port)"
        }
      } else {
        all_healthy = false
        {
          service: "cache"
          mode: "remote"
          status: "✗"
          message: $"Cannot reach cache service at ($host):($port)"
        }
      }
    }
    _ => {
      all_healthy = false
      {
        service: "cache"
        mode: $cache.mode
        status: "✗"
        message: $"Unknown cache mode: ($cache.mode)"
      }
    }
  })
  $results = ($results | append $cache_check)

  {
    all_healthy: $all_healthy
    services: $results
    timestamp: (date now)
  }
}

# ============================================================================
# Status Reporting
# ============================================================================

# Display status of services
export def show_status [services: list<string>] {
  log_section "Service Status"

  for service in $services {
    let service_info = ($SERVICES_REGISTRY | get $service)
    let is_running = (is_service_running $service)
    let status = (if $is_running { $"($COLOR_GREEN)✓ RUNNING($COLOR_RESET)" } else { $"($COLOR_RED)✗ STOPPED($COLOR_RESET)" })
    let port = $service_info.port

    print $"($service): $status (port $port)"
  }

  print ""
}

# Display service URLs for a list of started services
export def show_service_urls [services: list<string>] {
  log_info "Service URLs:"

  for service in $services {
    let service_info = ($SERVICES_REGISTRY | get $service)
    let port = $service_info.port
    let protocol = if (($port == 8081) or ($port == 8100)) { "grpc://" } else { "http://" }
    print $"  ($service): ($protocol)localhost:($port)"
  }

  print ""
}

# ============================================================================
# Configuration Parsing
# ============================================================================

# Get services to start based on configuration
export def get_services_to_start [services_set: string, custom_services: list<string>] {
  if $services_set == "custom" {
    $custom_services
  } else if $services_set == "all" {
    $SERVICE_GROUPS.all
  } else {
    $SERVICE_GROUPS.core
  }
}

# Validate that all requested services exist in registry
export def validate_services [services: list<string>] {
  let all_services = list_all_services
  let invalid = $services | where { |s| $s not-in $all_services }

  if ($invalid | length) > 0 {
    print $"Error: Unknown services: ($invalid | str join ', ')"
    print $"Available services: ($all_services | str join ', ')"
    []
  } else {
    $services
  }
}
