#!/usr/bin/env bash

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

function cmd::add::on_load() {
  flag::register --name
  flag::register --type
  flag::register --subtype
  flag::register --rule
  flag::register --group
  flag::register --ip
  flag::register --guest
  flag::register --tunnel
  flag::register --show-config
  flag::register --show-qr
}

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

function cmd::add::help() {
  cat <<EOF
Usage: wgctl add --name <name> --type <type> [options]

Add a new WireGuard client.

Options:
  --name <name>       Client name (e.g. nuno)
  --type <type>       Device type: desktop, laptop, phone, tablet, guest
  --subtype <subtype> Guest subtype: desktop, laptop, phone, tablet (mostly used for guest)
  --ip <ip>           Override auto-assigned IP (optional)
  --guest             Shorthand for --type guest
  --tunnel <mode>     Tunnel mode: split|full (default: split)
  --rule <rule>       Assign rule on creation (default: user, guest types: guest)
  --group <group>     Add to group on creation (group must exist)
  --show-config       Shows the WireGuard peer config
  --show-qr           Shows the WireGuard config in a QR Code

Device Types and Subnets:
  desktop   10.1.1.x
  laptop    10.1.2.x
  phone     10.1.3.x
  tablet    10.1.4.x
  guest     10.1.100.x

Rules:
  Automatically assigned based on type (guest → guest rule, others → user rule).
  Override with --rule. Manage rules with: wgctl rule help

Tunnel Modes:
  split     Route only VPN subnet + LAN through WireGuard (default)
  full      Route all traffic through WireGuard

Examples:
  wgctl add --name nuno --type phone
  wgctl add --name nuno --type laptop --ip 10.1.2.5
  wgctl add --name nuno --type phone --tunnel full
  wgctl add --name guest1 --type phone --guest
  wgctl add --name restricted --type desktop
  wgctl add --name dev --type laptop --rule dev-01
  wgctl add --name visitor --type guest --show-qr
EOF
}

# ============================================
# Validation
# ============================================

function cmd::add::validate() {
  local name="$1"
  local type="$2"
  local ip="$3"
  local tunnel="$4"

  if [[ -z "$name" ]]; then
    log::error "Missing required flag: --name"
    return 1
  fi

  if [[ -z "$type" ]]; then
    log::error "Missing required flag: --type"
    return 1
  fi

  if ! config::is_valid_type "$type"; then
    log::error "Invalid device type: ${type}"
    log::info  "Valid types: $(config::device_types | tr ' ' ', ')"
    return 1
  fi

  if [[ -n "$tunnel" && "$tunnel" != "split" && "$tunnel" != "full" ]]; then
    log::error "Invalid tunnel mode: ${tunnel} (use 'split' or 'full')"
    return 1
  fi

  if [[ -n "$ip" ]]; then
    ip::require_valid "$ip"
  fi

  local full_name="${type}-${name}"

  if [[ -f "$(ctx::clients)/${full_name}.conf" ]]; then
    log::error "Client already exists: ${full_name}"
    return 1
  fi
}

# ============================================
# Display helpers
# ============================================

function cmd::add::is_mobile() {
  local type="$1"
  [[ "$type" == "phone" || "$type" == "tablet" ]]
}

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

function cmd::add::run() {
  local name=""
  local type=""
  local subtype=""
  local rule=""
  local group=""
  local ip=""
  local tunnel=""
  local guest=false
  local show_config=false
  local show_qr=false

  # Parse flags
  while [[ $# -gt 0 ]]; do
    case "$1" in
      --name)         name="$2";           shift 2 ;;
      --type)         type="$2";           shift 2 ;;
      --subtype)      subtype="$2";        shift 2 ;;
      --rule)         rule="$2";           shift 2 ;;
      --group)        group="$2";          shift 2 ;;
      --ip)           ip="$2";             shift 2 ;;
      --guest)        guest=true;          shift   ;;
      --tunnel)       tunnel="$2";         shift 2 ;;
      --show-config)  show_config=true;    shift   ;;
      --show-qr)      show_qr=true;        shift   ;;
      --help)         cmd::add::help;      return  ;;
      *)
        log::error "Unknown flag: $1"
        cmd::add::help
        return 1
        ;;
    esac
  done

  $guest && type="guest"

  local effective_type
  effective_type=$(cmd::add::_resolve_type "$type" "$subtype") || return 1

  local full_name="${type}-${name}"
  cmd::add::validate "$name" "$type" "$ip" "$tunnel" || return 1

  [[ -z "$tunnel" ]] && tunnel=$(config::default_tunnel_for "$type")
  [[ -z "$rule"   ]] && rule=$(cmd::add::_default_rule "$type")

  rule::exists "$rule" || { log::error "Rule not found: ${rule}"; return 1; }

  local allowed_ips
  allowed_ips=$(config::allowed_ips_for "$effective_type" "$tunnel") || return 1

  log::section "Adding client: ${full_name}"
  [[ -z "$ip" ]] && ip=$(ip::next_for_type "$effective_type") || return 1

  log::wg_add "Name:   ${full_name}"
  log::wg_add "Type:   ${type}"
  log::wg_add "IP:     ${ip}"
  log::wg_add "Tunnel: ${tunnel} (${allowed_ips})"
  log::wg_add "Endpoint: $(config::endpoint)"
  log::wg_add "Rule:   ${rule:-none}"

  keys::generate_pair "$full_name"                                    || return 1
  peers::create_client_config "$full_name" "$effective_type" "$ip" "$allowed_ips" || return 1
  [[ -n "$subtype" ]] && peers::set_meta "$full_name" "subtype" "$subtype"
  
  if [[ -n "$group" ]]; then
    if ! group::exists "$group"; then
      log::wg_warning "Group '${group}' not found — skipping group assignment"
    else
      group::add_peer "$group" "$full_name"
      log::wg "Added to group: ${group}"
    fi
  fi

  local public_key
  public_key=$(keys::public "$full_name")                             || return 1
  peers::add_to_server "$full_name" "$public_key" "$ip"               || return 1

  [[ -n "$rule" ]] && rule::apply "$rule" "$ip"                       || return 1
  peers::reload                                                       || return 1

  log::wg_success "Client added successfully: ${full_name} (${ip}) [${tunnel} tunnel]"
  cmd::add::_show_result "$full_name" "${subtype:-$type}"
}

function cmd::add::_resolve_type() {
  local type="$1" subtype="$2"
  if [[ "$type" == "guest" && -n "$subtype" ]]; then
    local valid_subtypes="desktop laptop phone tablet"
    if ! echo "$valid_subtypes" | grep -qw "$subtype"; then
      log::error "Invalid subtype: ${subtype} (valid: desktop, laptop, phone, tablet)"
      return 1
    fi
    echo "guest-${subtype}"
  else
    echo "$type"
  fi
}

function cmd::add::_default_rule() {
  local type="$1"
  config::is_guest_type "$type" && echo "guest" || echo "user"
}

function cmd::add::_show_result() {
  local full_name="$1" display_type="$2"
  if cmd::add::is_mobile "$display_type"; then
    keys::qr "$full_name"
  else
    log::section "Client Config"
    cat "$(ctx::clients)/${full_name}.conf"
  fi
}