#!/usr/bin/env bash
# policy.command.sh — manage policies
#
# Subcommands:
#   wgctl policy list
#   wgctl policy show   --name <name>
#   wgctl policy add    --name <name> [--tunnel-mode split|full]
#                       [--default-rule <rule>] [--strict-rule] [--no-auto-apply]
#                       [--desc <desc>]
#   wgctl policy rm     --name <name>
#   wgctl policy set    --name <name> --field <field> --value <value>

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

function cmd::policy::on_load() {
  load_module policy

  flag::register --name
  flag::register --tunnel-mode
  flag::register --default-rule
  flag::register --strict-rule
  flag::register --no-strict-rule
  flag::register --auto-apply
  flag::register --no-auto-apply
  flag::register --desc
  flag::register --field
  flag::register --value

  command::mixin json_output
}

# ============================================
# Help
# ============================================

function cmd::policy::help() {
  cat <<EOF
Usage: wgctl policy <subcommand> [options]

Manage policies. Policies define behavioral flags for subnets and identities.

Subcommands:
  list                             List all policies
  show   --name <name>             Show policy details
  add    --name <name>             Add a new policy
         [--tunnel-mode split|full]
         [--default-rule <rule>]
         [--strict-rule]
         [--no-auto-apply]
         [--desc <desc>]
  rm     --name <name>             Remove a policy (built-ins cannot be removed)
  set    --name <name>             Set a single field on a policy
         --field <field>
         --value <value>

Fields:
  tunnel_mode    split|full
  default_rule   rule name (or empty to clear)
  strict_rule    true|false
  auto_apply     true|false
  desc           description string

Built-in policies (cannot be removed): default, guest, trusted, server, iot

Examples:
  wgctl policy list
  wgctl policy show --name guest
  wgctl policy add --name contractor --default-rule contractor --strict-rule
  wgctl policy set --name contractor --field tunnel-mode --value full
  wgctl policy rm --name contractor
EOF
}

# ============================================
# Run
# ============================================

function cmd::policy::run() {
  local subcmd="${1:-list}"
  shift || true

  if command::json; then
    cmd::policy::_output_json
    return 0
  fi

  case "$subcmd" in
    list)   cmd::policy::_list   "$@" ;;
    show)   cmd::policy::_show   "$@" ;;
    add)    cmd::policy::_add    "$@" ;;
    rm)     cmd::policy::_rm     "$@" ;;
    set)    cmd::policy::_set    "$@" ;;
    --help) cmd::policy::help        ;;
    *)
      log::error "Unknown subcommand '${subcmd}'. Available: list, show, add, rm, set"
      return 1
      ;;
  esac
}

# ============================================
# Subcommands
# ============================================

function cmd::policy::_list() {
  local data
  data=$(policy::list_data | ui::sort_rows 1)
 
  if [[ -z "$data" ]]; then
    log::info "No policies defined."
    return 0
  fi
 
  if display::is_table "policy_list"; then
    cmd::policy::_render_table "$data"
    return 0
  fi

  echo ""
  while IFS='|' read -r name tunnel default_rule strict auto desc; do
    ui::policy::list_row "$name" "$default_rule" "$strict" "$auto"
  done <<< "$data"
  echo ""
}

function cmd::policy::_render_table() {
  local data="${1:-}"
  [[ -z "$data" ]] && return 0
 
  ui::policy::list_header_table
  while IFS='|' read -r name tunnel default_rule strict auto desc; do
    [[ -z "$name" ]] && continue
    ui::policy::list_row_table "$name" "$tunnel" "$default_rule" "$strict" "$auto"
  done <<< "$data"
  printf "\n"
}
 

function cmd::policy::_show() {
  local name=""
  while [[ $# -gt 0 ]]; do
    case "$1" in
      --name) name="$2"; shift 2 ;;
      --help) cmd::policy::help; return ;;
      *) log::error "Unknown flag: $1"; return 1 ;;
    esac
  done
 
  [[ -z "$name" ]] && { log::error "Missing required flag: --name"; return 1; }
  policy::require_exists "$name" || return 1
 
  local dr tunnel strict auto
  dr=$(policy::default_rule "$name")
  tunnel=$(policy::tunnel_mode "$name")
  strict=$(policy::strict_rule "$name" && echo "yes" || echo "no")
  auto=$(policy::auto_apply "$name" && echo "yes" || echo "no")
 
  local rule_val="-"
  [[ -n "$dr" ]] && rule_val="$dr"
 
  local strict_padded
  strict_padded=$(printf "%-4s" "$strict")
 
  # First line — mirrors list format
  echo ""
  printf "  \033[1m%-14s\033[0m  \033[2mrule:\033[0m %-16s  \033[2mstrict:\033[0m %s\n" \
    "$name" "$rule_val" "$strict_padded"
  echo ""
 
  # Detail section
  local desc
  desc=$(policy::get "$name" "desc")
  [[ -n "$desc" ]]    && printf "    \033[2mDescription:\033[0m  %s\n" "$desc"
  printf "    \033[2mTunnel:\033[0m        %s\n" "$tunnel"
  printf "    \033[2mAuto apply:\033[0m    %s\n" "$auto"
  echo ""
}

function cmd::policy::_add() {
  local name="" tunnel_mode="split" default_rule="" \
        strict_rule="false" auto_apply="true" desc=""

  while [[ $# -gt 0 ]]; do
    case "$1" in
      --name)          name="$2";           shift 2 ;;
      --tunnel-mode)   tunnel_mode="$2";    shift 2 ;;
      --default-rule)  default_rule="$2";   shift 2 ;;
      --strict-rule)   strict_rule="true";  shift   ;;
      --no-strict-rule) strict_rule="false"; shift  ;;
      --no-auto-apply) auto_apply="false";  shift   ;;
      --auto-apply)    auto_apply="true";   shift   ;;
      --desc)          desc="$2";           shift 2 ;;
      --help)          cmd::policy::help;   return  ;;
      *) log::error "Unknown flag: $1"; return 1    ;;
    esac
  done

  [[ -z "$name" ]] && { log::error "Missing required flag: --name"; return 1; }

  case "$tunnel_mode" in
    split|full) ;;
    *) log::error "Invalid --tunnel-mode '${tunnel_mode}'. Use: split, full"; return 1 ;;
  esac

  json::policy_add "$(ctx::policies)" "$name" "$tunnel_mode" \
    "$default_rule" "$strict_rule" "$auto_apply" "$desc"
  log::ok "Policy '${name}' added"
}

function cmd::policy::_rm() {
  local name=""
  while [[ $# -gt 0 ]]; do
    case "$1" in
      --name) name="$2"; shift 2 ;;
      --help) cmd::policy::help; return ;;
      *) log::error "Unknown flag: $1"; return 1 ;;
    esac
  done

  [[ -z "$name" ]] && { log::error "Missing required flag: --name"; return 1; }
  policy::require_exists "$name" || return 1

  json::policy_remove "$(ctx::policies)" "$name"
  log::ok "Policy '${name}' removed"
}

function cmd::policy::_set() {
  local name="" field="" value=""
  while [[ $# -gt 0 ]]; do
    case "$1" in
      --name)  name="$2";  shift 2 ;;
      --field) field="$2"; shift 2 ;;
      --value) value="$2"; shift 2 ;;
      --help)  cmd::policy::help; return ;;
      *) log::error "Unknown flag: $1"; return 1 ;;
    esac
  done

  [[ -z "$name"  ]] && { log::error "Missing required flag: --name";  return 1; }
  [[ -z "$field" ]] && { log::error "Missing required flag: --field"; return 1; }
  [[ -z "$value" ]] && { log::error "Missing required flag: --value"; return 1; }

  policy::require_exists "$name" || return 1

  # Normalise field name (allow tunnel-mode as well as tunnel_mode)
  field="${field//-/_}"

  case "$field" in
    tunnel_mode)
      case "$value" in
        split|full) ;;
        *) log::error "Invalid value '${value}' for tunnel_mode. Use: split, full"; return 1 ;;
      esac
      ;;
    strict_rule|auto_apply)
      case "$value" in
        true|false) ;;
        *) log::error "Invalid value '${value}' for ${field}. Use: true, false"; return 1 ;;
      esac
      ;;
    default_rule|desc) ;;
    *)
      log::error "Unknown field '${field}'. Valid: tunnel_mode, default_rule, strict_rule, auto_apply, desc"
      return 1
      ;;
  esac

  json::policy_set_field "$(ctx::policies)" "$name" "$field" "$value"
  log::ok "Policy '${name}': ${field} = ${value}"
}

function cmd::policy::_output_json() {
  local data
  data=$(policy::list_data 2>/dev/null)
 
  local -a policies=()
  while IFS='|' read -r name tunnel_mode default_rule strict_rule auto_apply desc; do
    [[ -z "$name" ]] && continue
 
    local strict_json="false"
    [[ "$strict_rule" == "true" ]] && strict_json="true"
    local auto_json="true"
    [[ "$auto_apply" == "false" ]] && auto_json="false"
 
    policies+=("$(printf '{"name":"%s","tunnel_mode":"%s","default_rule":"%s","strict_rule":%s,"auto_apply":%s,"desc":"%s"}' \
      "$name" "$tunnel_mode" "${default_rule:-}" \
      "$strict_json" "$auto_json" "$desc")")
  done <<< "$data"
 
  local count=${#policies[@]}
  local array
  array=$(printf '%s\n' "${policies[@]:-}" | paste -sd ',' -)
  printf '{"policies":[%s]}' "${array:-}" | json::envelope "policy list" "$count"
}