#!/usr/bin/env bash

# ============================================
# Lifecycle
# ============================================

function config::on_load() {
  config::_init_defaults
  config::load
  config::validate
  fmt::set_date_format "${_FMT_DATE_FORMAT:-eu}"
}

# ============================================
# Defaults
# ============================================

declare -g _ACTIVITY_TOTAL_LOW_BYTES="${ACTIVITY_TOTAL_LOW_BYTES:-1000000}"
declare -g _ACTIVITY_TOTAL_MED_BYTES="${ACTIVITY_TOTAL_MED_BYTES:-10000000}"
declare -g _ACTIVITY_TOTAL_HIGH_BYTES="${ACTIVITY_TOTAL_HIGH_BYTES:-100000000}"
declare -g _ACTIVITY_CURRENT_LOW_BYTES="${ACTIVITY_CURRENT_LOW_BYTES:-1000000}"
declare -g _ACTIVITY_CURRENT_MED_BYTES="${ACTIVITY_CURRENT_MED_BYTES:-10000000}"
declare -g _ACTIVITY_CURRENT_HIGH_BYTES="${ACTIVITY_CURRENT_HIGH_BYTES:-100000000}"

declare -gA _COMMAND_DEFAULTS=()
declare -gA _COMMAND_ALIASES=()

function config::_init_defaults() {
  _WG_INTERFACE="wg0"
  _WG_DNS="10.0.0.103"
  _WG_DNS_FALLBACK=""
  _WG_LAN="10.0.0.0/24"
  _WG_SUBNET="10.1.0.0/16"
  _WG_PORT="51820"
  _WG_ENDPOINT=""
  _WG_HANDSHAKE_CHECK_TIME_SEC="300"
  _FMT_DATE_FORMAT="eu"

  # Derived
  _WG_CONFIG="$(ctx::wg)/${_WG_INTERFACE}.conf"
  _WG_SERVER_PUBLIC_KEY_FILE="$(ctx::wg)/server_public.key"
  _WG_SERVER_PRIVATE_KEY_FILE="$(ctx::wg)/server_private.key"
  _WG_TUNNEL_SPLIT="${_WG_SUBNET}, ${_WG_LAN}"
  _WG_TUNNEL_FULL="0.0.0.0/0, ::/0"
}

# ============================================
# Load from wgctl.json
# Falls back to wgctl.conf for migration period
# ============================================

function config::load() {
  local json_conf
  json_conf="$(ctx::config_file)"

  if [[ -f "$json_conf" ]]; then
    config::_load_json "$json_conf"
  else
    # Fallback: legacy wgctl.conf
    local legacy_conf
    legacy_conf="$(ctx::wgctl)/wgctl.conf"
    [[ -f "$legacy_conf" ]] && config::_load_legacy "$legacy_conf"
  fi

  # Recompute derived values after overrides
  _WG_CONFIG="$(ctx::wg)/${_WG_INTERFACE}.conf"
  _WG_TUNNEL_SPLIT="${_WG_SUBNET}, ${_WG_LAN}"
}

function config::_load_json() {
  local file="$1"
  [[ ! -f "$file" ]] && return 0

  while IFS='=' read -r key value; do
    [[ -z "$key" ]] && continue
    case "$key" in
      WG_INTERFACE)                 _WG_INTERFACE="$value"                ;;
      WG_ENDPOINT)                  _WG_ENDPOINT="$value"                 ;;
      WG_DNS)                       _WG_DNS="$value"                      ;;
      WG_DNS_FALLBACK)              _WG_DNS_FALLBACK="$value"             ;;
      WG_PORT)                      _WG_PORT="$value"                     ;;
      WG_SUBNET)                    _WG_SUBNET="$value"                   ;;
      WG_LAN)                       _WG_LAN="$value"                      ;;
      WG_HANDSHAKE_CHECK_TIME_SEC)  _WG_HANDSHAKE_CHECK_TIME_SEC="$value" ;;
      DATE_FORMAT)
        _FMT_DATE_FORMAT="$value"
        fmt::set_date_format "$value"
        ;;
      ACTIVITY_TOTAL_LOW_BYTES)     _ACTIVITY_TOTAL_LOW_BYTES="$value"    ;;
      ACTIVITY_TOTAL_MED_BYTES)     _ACTIVITY_TOTAL_MED_BYTES="$value"    ;;
      ACTIVITY_TOTAL_HIGH_BYTES)    _ACTIVITY_TOTAL_HIGH_BYTES="$value"   ;;
      ACTIVITY_CURRENT_LOW_BYTES)   _ACTIVITY_CURRENT_LOW_BYTES="$value"  ;;
      ACTIVITY_CURRENT_MED_BYTES)   _ACTIVITY_CURRENT_MED_BYTES="$value"  ;;
      ACTIVITY_CURRENT_HIGH_BYTES)  _ACTIVITY_CURRENT_HIGH_BYTES="$value" ;;
      CMD_DEFAULT:*)
        local cmd_name="${key#CMD_DEFAULT:}"
        _COMMAND_DEFAULTS["$cmd_name"]="$value"
        ;;
      CMD_ALIAS:*)
        local alias_name="${key#CMD_ALIAS:}"
        _COMMAND_ALIASES["$alias_name"]="$value"
        ;;
    esac
  done < <(json::config_load "$file" 2>/dev/null)
}

function config::_load_legacy() {
  local conf_file="$1"
  log::wg_warning "Using legacy wgctl.conf — run 'wgctl config migrate' to upgrade"
  while IFS='=' read -r key value || [[ -n "$key" ]]; do
    [[ "$key" =~ ^[[:space:]]*# ]] && continue
    [[ -z "${key// }" ]] && continue
    key="${key// /}"
    value="${value// /}"
    case "$key" in
      WG_INTERFACE)                               _WG_INTERFACE="$value"                ;;
      WG_ENDPOINT)                                _WG_ENDPOINT="$value"                 ;;
      WG_DNS)                                     _WG_DNS="$value"                      ;;
      WG_DNS_FALLBACK)                            _WG_DNS_FALLBACK="$value"             ;;
      WG_PORT)                                    _WG_PORT="$value"                     ;;
      WG_SUBNET)                                  _WG_SUBNET="$value"                   ;;
      WG_LAN)                                     _WG_LAN="$value"                      ;;
      WG_HANDSHAKE_CHECK_TIME_SEC)                _WG_HANDSHAKE_CHECK_TIME_SEC="$value" ;;
      DATE_FORMAT)
        _FMT_DATE_FORMAT="$value"
        fmt::set_date_format "$value"
        ;;
    esac
  done < "$conf_file"
}

# ============================================
# Validation (unchanged)
# ============================================

function config::validate() {
  local errors=()

  if [[ ! -f "$_WG_SERVER_PUBLIC_KEY_FILE" ]]; then
    errors+=("Server public key not found: ${_WG_SERVER_PUBLIC_KEY_FILE}")
  fi
  if [[ ! -f "$_WG_SERVER_PRIVATE_KEY_FILE" ]]; then
    errors+=("Server private key not found: ${_WG_SERVER_PRIVATE_KEY_FILE}")
  fi
  if [[ ! -f "$_WG_CONFIG" ]]; then
    errors+=("WireGuard config not found: ${_WG_CONFIG}")
  fi

  local endpoint
  endpoint=$(config::endpoint)
  if [[ -z "$endpoint" ]]; then
    errors+=("WG_ENDPOINT is not set — required for client config generation")
  elif [[ "$endpoint" != *:* ]]; then
    errors+=("WG_ENDPOINT must include port (e.g. wg.example.com:51820)")
  fi

  local port
  port=$(config::port)
  if [[ -z "$port" ]]; then
    errors+=("WG_PORT is not set")
  elif ! [[ "$port" =~ ^[0-9]+$ ]] || (( port < 1 || port > 65535 )); then
    errors+=("WG_PORT must be a valid port number (1-65535)")
  fi

  local dns
  dns=$(config::dns)
  if [[ -z "$dns" ]]; then
    errors+=("WG_DNS is not set — required for client configs")
  elif ! ip::is_valid "$dns"; then
    errors+=("WG_DNS must be a valid IP address")
  fi

  local subnet
  subnet=$(config::subnet)
  if [[ -z "$subnet" ]]; then
    errors+=("WG_SUBNET is not set — required for IP allocation")
  fi

  local lan
  lan=$(config::lan)
  if [[ -z "$lan" ]]; then
    log::wg_warning "WG_LAN is not set — some rule features may not work correctly"
  fi

  if [[ ${#errors[@]} -gt 0 ]]; then
    log::error "wgctl configuration errors:"
    for err in "${errors[@]}"; do
      printf "  ✗ %s\n" "$err" >&2
    done
    printf "\n  Edit %s to fix these issues.\n\n" "$(ctx::config_file)" >&2
    return 1
  fi

  return 0
}

# ============================================
# Accessors (unchanged)
# ============================================

function config::interface()              { echo "$_WG_INTERFACE"; }
function config::config_file()            { echo "$_WG_CONFIG"; }
function config::endpoint()               { echo "$_WG_ENDPOINT"; }
function config::dns()                    { echo "$_WG_DNS"; }
function config::dns_fallback()           { echo "${_WG_DNS_FALLBACK:-}"; }
function config::port()                   { echo "$_WG_PORT"; }
function config::subnet()                 { echo "$_WG_SUBNET"; }
function config::lan()                    { echo "$_WG_LAN"; }
function config::tunnel_split()           { echo "$_WG_TUNNEL_SPLIT"; }
function config::tunnel_full()            { echo "$_WG_TUNNEL_FULL"; }
function config::handshake_time_sec()     { echo "$_WG_HANDSHAKE_CHECK_TIME_SEC"; }
function config::activity_total_low()     { echo "$_ACTIVITY_TOTAL_LOW_BYTES"; }
function config::activity_total_med()     { echo "$_ACTIVITY_TOTAL_MED_BYTES"; }
function config::activity_total_high()    { echo "$_ACTIVITY_TOTAL_HIGH_BYTES"; }
function config::activity_current_low()   { echo "$_ACTIVITY_CURRENT_LOW_BYTES"; }
function config::activity_current_med()   { echo "$_ACTIVITY_CURRENT_MED_BYTES"; }
function config::activity_current_high()  { echo "$_ACTIVITY_CURRENT_HIGH_BYTES"; }
function config::server_public_key()      { cat "$_WG_SERVER_PUBLIC_KEY_FILE"; }

function config::allowed_ips_for() {
  local tunnel="${1:-split}"
  case "$tunnel" in
    full)  echo "$_WG_TUNNEL_FULL"  ;;
    split) echo "$_WG_TUNNEL_SPLIT" ;;
    *)
      log::error "Unknown tunnel mode: ${tunnel} (use 'split' or 'full')"
      return 1
      ;;
  esac
}

function config::dns_string() {
  local fallback
  fallback=$(config::dns_fallback)
  if [[ -n "$fallback" ]]; then
    echo "$(config::dns), ${fallback}"
  else
    echo "$(config::dns)"
  fi
}