package main

import (
    "encoding/json"
    "net/http"
    "fmt"

    "github.com/gorilla/mux"
    "gorm.io/gorm"
    qrcode "github.com/skip2/go-qrcode"
)

// Helper wrappers
func respondJSON(w http.ResponseWriter, status int, payload interface{}) {
    w.Header().Set("Content-Type", "application/json")
    w.WriteHeader(status)
    _ = json.NewEncoder(w).Encode(payload)
}

func respondError(w http.ResponseWriter, status int, message string) {
    respondJSON(w, status, map[string]string{"error": message})
}

// ListServers lists all registered WireGuard servers.
// @Summary List all servers
// @Description Returns a list of all registered WireGuard servers.
// @Tags servers
// @Accept json
// @Produce json
// @Success 200 {array} Server
// @Failure 500 {object} map[string]string
// @Security ApiKeyAuth
// @Security BearerAuth
// @Router /api/servers [get]
func getServers(w http.ResponseWriter, r *http.Request) {
    var servers []Server
    if err := db.Find(&servers).Error; err != nil {
        respondError(w, http.StatusInternalServerError, err.Error())
        return
    }
    respondJSON(w, http.StatusOK, servers)
}

// CreateServer creates a new WireGuard server entry.
// @Summary Create a server
// @Description Creates a new WireGuard server with the provided configuration.
// @Tags servers
// @Accept json
// @Produce json
// @Param server body Server true "Server configuration"
// @Success 201 {object} Server
// @Failure 400 {object} map[string]string
// @Failure 500 {object} map[string]string
// @Security ApiKeyAuth
// @Security BearerAuth
// @Router /api/servers [post]
func createServer(w http.ResponseWriter, r *http.Request) {
    var s Server
    if err := json.NewDecoder(r.Body).Decode(&s); err != nil {
        respondError(w, http.StatusBadRequest, "invalid request payload")
        return
    }
    // Input validation
    if !ValidatePublicKey(s.PublicKey) {
        respondError(w, http.StatusBadRequest, "invalid public key")
        return
    }
    if err := db.Create(&s).Error; err != nil {
        respondError(w, http.StatusInternalServerError, err.Error())
        return
    }
    respondJSON(w, http.StatusCreated, s)
}

// GetServer retrieves a single WireGuard server by ID.
// @Summary Get a server
// @Description Returns a single WireGuard server by its ID.
// @Tags servers
// @Produce json
// @Param id path string true "Server ID"
// @Success 200 {object} Server
// @Failure 404 {object} map[string]string
// @Failure 500 {object} map[string]string
// @Security ApiKeyAuth
// @Security BearerAuth
// @Router /api/servers/{id} [get]
func getServer(w http.ResponseWriter, r *http.Request) {
    vars := mux.Vars(r)
    id := vars["id"]
    var s Server
    if err := db.First(&s, "id = ?", id).Error; err != nil {
        if err == gorm.ErrRecordNotFound {
            respondError(w, http.StatusNotFound, "server not found")
        } else {
            respondError(w, http.StatusInternalServerError, err.Error())
        }
        return
    }
    respondJSON(w, http.StatusOK, s)
}

// UpdateServer updates an existing WireGuard server.
// @Summary Update a server
// @Description Updates fields of an existing WireGuard server.
// @Tags servers
// @Accept json
// @Produce json
// @Param id path string true "Server ID"
// @Param server body Server true "Updated server fields"
// @Success 200 {object} Server
// @Failure 400 {object} map[string]string
// @Failure 404 {object} map[string]string
// @Failure 500 {object} map[string]string
// @Security ApiKeyAuth
// @Security BearerAuth
// @Router /api/servers/{id} [put]
func updateServer(w http.ResponseWriter, r *http.Request) {
    vars := mux.Vars(r)
    id := vars["id"]
    var s Server
    if err := db.First(&s, "id = ?", id).Error; err != nil {
        if err == gorm.ErrRecordNotFound {
            respondError(w, http.StatusNotFound, "server not found")
        } else {
            respondError(w, http.StatusInternalServerError, err.Error())
        }
        return
    }
    var updates Server
    if err := json.NewDecoder(r.Body).Decode(&updates); err != nil {
        respondError(w, http.StatusBadRequest, "invalid request payload")
        return
    }
    // Input validation for updated fields
    if updates.PublicKey != "" && !ValidatePublicKey(updates.PublicKey) {
        respondError(w, http.StatusBadRequest, "invalid public key")
        return
    }
    // Apply updates using fields defined in models.go
    s.Name = updates.Name
    s.Mode = updates.Mode
    s.PublicKey = updates.PublicKey
    s.Endpoint = updates.Endpoint
    if err := db.Save(&s).Error; err != nil {
        respondError(w, http.StatusInternalServerError, err.Error())
        return
    }
    respondJSON(w, http.StatusOK, s)
}

// DeleteServer removes a WireGuard server and cascades to its peers and webhooks.
// @Summary Delete a server
// @Description Deletes a WireGuard server and cascade-removes its peers and webhooks.
// @Tags servers
// @Produce json
// @Param id path string true "Server ID"
// @Success 204 "No content"
// @Failure 404 {object} map[string]string
// @Failure 500 {object} map[string]string
// @Security ApiKeyAuth
// @Security BearerAuth
// @Router /api/servers/{id} [delete]
func deleteServer(w http.ResponseWriter, r *http.Request) {
    vars := mux.Vars(r)
    id := vars["id"]
    var s Server
    if err := db.First(&s, "id = ?", id).Error; err != nil {
        if err == gorm.ErrRecordNotFound {
            respondError(w, http.StatusNotFound, "server not found")
        } else {
            respondError(w, http.StatusInternalServerError, err.Error())
        }
        return
    }
    if err := db.Delete(&s).Error; err != nil {
        respondError(w, http.StatusInternalServerError, err.Error())
        return
    }
    respondJSON(w, http.StatusNoContent, nil)
}

// ListServerPeers lists all peers for a given WireGuard server.
// @Summary List server peers
// @Description Returns all peers belonging to a specific WireGuard server.
// @Tags peers
// @Produce json
// @Param id path string true "Server ID"
// @Success 200 {array} Peer
// @Failure 404 {object} map[string]string
// @Failure 500 {object} map[string]string
// @Security ApiKeyAuth
// @Security BearerAuth
// @Router /api/servers/{id}/peers [get]
func getServerPeers(w http.ResponseWriter, r *http.Request) {
    vars := mux.Vars(r)
    id := vars["id"]
    // Ensure server exists
    var s Server
    if err := db.First(&s, "id = ?", id).Error; err != nil {
        if err == gorm.ErrRecordNotFound {
            respondError(w, http.StatusNotFound, "server not found")
        } else {
            respondError(w, http.StatusInternalServerError, err.Error())
        }
        return
    }
    var peers []Peer
    if err := db.Where("server_id = ?", id).Find(&peers).Error; err != nil {
        respondError(w, http.StatusInternalServerError, err.Error())
        return
    }
    respondJSON(w, http.StatusOK, peers)
}

// CreateServerPeer creates a new peer for a WireGuard server.
// @Summary Create a peer
// @Description Creates a new WireGuard peer. Applies nftables rules in forward mode or triggers webhooks in standalone mode.
// @Tags peers
// @Accept json
// @Produce json
// @Param id path string true "Server ID"
// @Param peer body Peer true "Peer configuration"
// @Success 201 {object} Peer
// @Failure 400 {object} map[string]string
// @Failure 404 {object} map[string]string
// @Failure 500 {object} map[string]string
// @Security ApiKeyAuth
// @Security BearerAuth
// @Router /api/servers/{id}/peers [post]
func createServerPeer(w http.ResponseWriter, r *http.Request) {
    vars := mux.Vars(r)
    id := vars["id"]
    // ensure server exists
    var s Server
    if err := db.First(&s, "id = ?", id).Error; err != nil {
        if err == gorm.ErrRecordNotFound {
            respondError(w, http.StatusNotFound, "server not found")
        } else {
            respondError(w, http.StatusInternalServerError, err.Error())
        }
        return
    }
    var p Peer
    if err := json.NewDecoder(r.Body).Decode(&p); err != nil {
        respondError(w, http.StatusBadRequest, "invalid request payload")
        return
    }
    // Input validation for peer
    if !ValidatePublicKey(p.PublicKey) {
        respondError(w, http.StatusBadRequest, "invalid public key")
        return
    }
    if !ValidateIP(p.IP) {
        respondError(w, http.StatusBadRequest, "invalid ip address for peer IP")
        return
    }
    // Optional: validate AllowAccess CIDRs if provided
    if len(p.AllowAccess) > 0 {
        var targets []string
        if err := json.Unmarshal(p.AllowAccess, &targets); err == nil {
            for _, t := range targets {
                if !ValidateCIDR(t) {
                    respondError(w, http.StatusBadRequest, "invalid CIDR in AllowAccess")
                    return
                }
            }
        }
    }
    p.ServerID = s.ID
    if err := db.Create(&p).Error; err != nil {
        respondError(w, http.StatusInternalServerError, err.Error())
        return
    }
    // Trigger mode-specific actions after creation
    policyChanges := []string{"peer_created"}
    if s.Mode == "forward" {
        // Apply nftables rules for the new peer
        if len(p.AllowAccess) > 0 {
            var targets []string
            if err := json.Unmarshal(p.AllowAccess, &targets); err == nil {
                for _, t := range targets {
                    _ = AddAccessRule(p.IP, t)
                }
            }
        }
        _ = SetInternetAccess(p.IP, p.AllowInternet)
        // Notify policy change
        _ = TriggerWebhook("policy_changed", &s, &p, "created", policyChanges)
    } else if s.Mode == "standalone" {
        // Notify remote via webhooks
        _ = TriggerWebhook("peer_created", &s, &p, "created", []string{})
        // Also notify policy change
        _ = TriggerWebhook("policy_changed", &s, &p, "created", policyChanges)
    }
    respondJSON(w, http.StatusCreated, p)
}

// UpdatePeer updates an existing WireGuard peer.
// @Summary Update a peer
// @Description Updates a peer and computes diffs to apply incremental nftables changes.
// @Tags peers
// @Accept json
// @Produce json
// @Param id path string true "Peer ID"
// @Param peer body Peer true "Updated peer fields"
// @Success 200 {object} Peer
// @Failure 400 {object} map[string]string
// @Failure 404 {object} map[string]string
// @Failure 500 {object} map[string]string
// @Security ApiKeyAuth
// @Security BearerAuth
// @Router /api/peers/{id} [put]
func updatePeer(w http.ResponseWriter, r *http.Request) {
    vars := mux.Vars(r)
    id := vars["id"]
    var p Peer
    if err := db.First(&p, "id = ?", id).Error; err != nil {
        if err == gorm.ErrRecordNotFound {
            respondError(w, http.StatusNotFound, "peer not found")
        } else {
            respondError(w, http.StatusInternalServerError, err.Error())
        }
        return
    }
    var updates Peer
    if err := json.NewDecoder(r.Body).Decode(&updates); err != nil {
        respondError(w, http.StatusBadRequest, "invalid request payload")
        return
    }
    // Preserve the original peer for diff computation
    oldP := p
    // Validate updated fields
    if updates.PublicKey != "" && !ValidatePublicKey(updates.PublicKey) {
        respondError(w, http.StatusBadRequest, "invalid public key")
        return
    }
    if updates.IP != "" && !ValidateIP(updates.IP) {
        respondError(w, http.StatusBadRequest, "invalid ip address for peer IP")
        return
    }
    if len(updates.AllowAccess) > 0 {
        var targets []string
        if err := json.Unmarshal(updates.AllowAccess, &targets); err == nil {
            for _, t := range targets {
                if !ValidateCIDR(t) {
                    respondError(w, http.StatusBadRequest, "invalid CIDR in AllowAccess")
                    return
                }
            }
        }
    }
    // Build a list of policy-related changes for notification purposes
    changes := []string{}
    if updates.PublicKey != "" && updates.PublicKey != p.PublicKey {
        changes = append(changes, "PublicKey updated")
    }
    if updates.IP != "" && updates.IP != p.IP {
        changes = append(changes, "IP updated")
    }
    if len(updates.AllowAccess) > 0 && string(updates.AllowAccess) != string(p.AllowAccess) {
        changes = append(changes, "AllowAccess updated")
    }
    if updates.AllowInternet != p.AllowInternet {
        changes = append(changes, "AllowInternet updated")
    }
    // Apply updates based on the Peer model fields
    p.PublicKey = updates.PublicKey
    p.IP = updates.IP
    p.AllowAccess = updates.AllowAccess
    p.AllowInternet = updates.AllowInternet
    if err := db.Save(&p).Error; err != nil {
        respondError(w, http.StatusInternalServerError, err.Error())
        return
    }
    // If policy-related fields changed, apply mode-specific actions
    if len(changes) > 0 {
        var server Server
        if err := db.First(&server, "id = ?", p.ServerID).Error; err == nil {
            if server.Mode == "forward" {
                // nftables-based updates: diff AllowAccess and IP/internet changes
                // Compute targets diffs between old and new
                var oldTargets []string
                if len(oldP.AllowAccess) > 0 {
                    _ = json.Unmarshal(oldP.AllowAccess, &oldTargets)
                }
                var newTargets []string
                if len(p.AllowAccess) > 0 {
                    _ = json.Unmarshal(p.AllowAccess, &newTargets)
                }
                // Determine additions/removals
                added := []string{}
                for _, t := range newTargets {
                    found := false
                    for _, o := range oldTargets {
                        if o == t { found = true; break }
                    }
                    if !found { added = append(added, t) }
                }
                removed := []string{}
                for _, o := range oldTargets {
                    found := false
                    for _, t := range newTargets {
                        if o == t { found = true; break }
                    }
                    if !found { removed = append(removed, o) }
                }
                // Apply removals first for safety
                fromIP := oldP.IP
                if oldP.IP == "" {
                    fromIP = p.IP
                }
                for _, t := range removed {
                    _ = RemoveAccessRule(fromIP, t)
                }
                // Apply additions for the current IP
                toIP := p.IP
                for _, t := range added {
                    _ = AddAccessRule(toIP, t)
                }
                // Internet access flag
                _ = SetInternetAccess(p.IP, p.AllowInternet)
                // Notify policy change
                _ = TriggerWebhook("policy_changed", &server, &p, "updated", changes)
            } else if server.Mode == "standalone" {
                _ = TriggerWebhook("peer_updated", &server, &p, "updated", changes)
                // Also notify policy change
                _ = TriggerWebhook("policy_changed", &server, &p, "updated", changes)
            }
        }
    }
    respondJSON(w, http.StatusOK, p)
}

// DeletePeer removes a WireGuard peer and cleans up nftables rules.
// @Summary Delete a peer
// @Description Deletes a peer, cleans up nftables rules, and triggers webhooks.
// @Tags peers
// @Produce json
// @Param id path string true "Peer ID"
// @Success 204 "No content"
// @Failure 404 {object} map[string]string
// @Failure 500 {object} map[string]string
// @Security ApiKeyAuth
// @Security BearerAuth
// @Router /api/peers/{id} [delete]
func deletePeer(w http.ResponseWriter, r *http.Request) {
    vars := mux.Vars(r)
    id := vars["id"]
    var p Peer
    if err := db.First(&p, "id = ?", id).Error; err != nil {
        if err == gorm.ErrRecordNotFound {
            respondError(w, http.StatusNotFound, "peer not found")
        } else {
            respondError(w, http.StatusInternalServerError, err.Error())
        }
        return
    }
    // If in forward mode, remove any NFTables rules for this peer before deletion
    var server Server
    if err := db.First(&server, "id = ?", p.ServerID).Error; err == nil {
        if server.Mode == "forward" {
            var targets []string
            if len(p.AllowAccess) > 0 {
                _ = json.Unmarshal(p.AllowAccess, &targets)
            }
            for _, t := range targets {
                _ = RemoveAccessRule(p.IP, t)
            }
            _ = SetInternetAccess(p.IP, false)
            // Notify policy change
            _ = TriggerWebhook("policy_changed", &server, &p, "deleted", []string{"peer_deleted"})
        }
    }
    if err := db.Delete(&p).Error; err != nil {
        respondError(w, http.StatusInternalServerError, err.Error())
        return
    }
    // Notify external systems about the deletion according to server mode
    if err := db.First(&server, "id = ?", p.ServerID).Error; err == nil {
        if server.Mode == "standalone" {
            _ = TriggerWebhook("peer_deleted", &server, &p, "deleted", []string{})
            // Also notify policy change
            _ = TriggerWebhook("policy_changed", &server, &p, "deleted", []string{"peer_deleted"})
        }
    }
    respondJSON(w, http.StatusNoContent, nil)
}

// GetSMTPSettings retrieves the current SMTP configuration.
// @Summary Get SMTP settings
// @Description Returns the current SMTP configuration for email notifications.
// @Tags settings
// @Produce json
// @Success 200 {object} SMTPSettings
// @Failure 500 {object} map[string]string
// @Security ApiKeyAuth
// @Security BearerAuth
// @Router /api/settings/smtp [get]
func getSMTPSettings(w http.ResponseWriter, r *http.Request) {
    settings, err := GetSMTPSettings()
    if err != nil {
        respondError(w, http.StatusInternalServerError, err.Error())
        return
    }
    respondJSON(w, http.StatusOK, settings)
}

// SetSMTPSettings saves SMTP configuration for email notifications.
// @Summary Save SMTP settings
// @Description Saves or updates SMTP configuration for email notifications.
// @Tags settings
// @Accept json
// @Produce json
// @Param settings body SMTPSettings true "SMTP configuration"
// @Success 200 {object} SMTPSettings
// @Failure 400 {object} map[string]string
// @Failure 500 {object} map[string]string
// @Security ApiKeyAuth
// @Security BearerAuth
// @Router /api/settings/smtp [post]
func setSMTPSettings(w http.ResponseWriter, r *http.Request) {
    var s SMTPSettings
    if err := json.NewDecoder(r.Body).Decode(&s); err != nil {
        respondError(w, http.StatusBadRequest, "invalid request payload")
        return
    }
    if err := SaveSMTPSettings(s); err != nil {
        respondError(w, http.StatusInternalServerError, err.Error())
        return
    }
    respondJSON(w, http.StatusOK, s)
}

// ListServerWebhooks lists all webhooks for a WireGuard server.
// @Summary List server webhooks
// @Description Returns all webhooks configured for a specific WireGuard server.
// @Tags webhooks
// @Produce json
// @Param id path string true "Server ID"
// @Success 200 {array} Webhook
// @Failure 404 {object} map[string]string
// @Failure 500 {object} map[string]string
// @Security ApiKeyAuth
// @Security BearerAuth
// @Router /api/servers/{id}/webhooks [get]
func getServerWebhooks(w http.ResponseWriter, r *http.Request) {
    vars := mux.Vars(r)
    id := vars["id"]
    var s Server
    if err := db.First(&s, "id = ?", id).Error; err != nil {
        if err == gorm.ErrRecordNotFound {
            respondError(w, http.StatusNotFound, "server not found")
        } else {
            respondError(w, http.StatusInternalServerError, err.Error())
        }
        return
    }
    var webhooks []Webhook
    if err := db.Where("server_id = ?", id).Find(&webhooks).Error; err != nil {
        respondError(w, http.StatusInternalServerError, err.Error())
        return
    }
    respondJSON(w, http.StatusOK, webhooks)
}

// CreateServerWebhook creates a new webhook for a WireGuard server.
// @Summary Create a webhook
// @Description Creates a new webhook configuration for a WireGuard server.
// @Tags webhooks
// @Accept json
// @Produce json
// @Param id path string true "Server ID"
// @Param webhook body Webhook true "Webhook configuration"
// @Success 201 {object} Webhook
// @Failure 400 {object} map[string]string
// @Failure 404 {object} map[string]string
// @Failure 500 {object} map[string]string
// @Security ApiKeyAuth
// @Security BearerAuth
// @Router /api/servers/{id}/webhooks [post]
func createServerWebhook(w http.ResponseWriter, r *http.Request) {
    vars := mux.Vars(r)
    id := vars["id"]
    var s Server
    if err := db.First(&s, "id = ?", id).Error; err != nil {
        if err == gorm.ErrRecordNotFound {
            respondError(w, http.StatusNotFound, "server not found")
        } else {
            respondError(w, http.StatusInternalServerError, err.Error())
        }
        return
    }
    var wb Webhook
    if err := json.NewDecoder(r.Body).Decode(&wb); err != nil {
        respondError(w, http.StatusBadRequest, "invalid request payload")
        return
    }
    wb.ServerID = s.ID
    if err := db.Create(&wb).Error; err != nil {
        respondError(w, http.StatusInternalServerError, err.Error())
        return
    }
    respondJSON(w, http.StatusCreated, wb)
}

// DeleteWebhook removes a webhook configuration.
// @Summary Delete a webhook
// @Description Deletes a webhook by its ID.
// @Tags webhooks
// @Produce json
// @Param id path string true "Webhook ID"
// @Success 204 "No content"
// @Failure 404 {object} map[string]string
// @Failure 500 {object} map[string]string
// @Security ApiKeyAuth
// @Security BearerAuth
// @Router /api/webhooks/{id} [delete]
func deleteWebhook(w http.ResponseWriter, r *http.Request) {
    vars := mux.Vars(r)
    id := vars["id"]
    var wb Webhook
    if err := db.First(&wb, "id = ?", id).Error; err != nil {
        if err == gorm.ErrRecordNotFound {
            respondError(w, http.StatusNotFound, "webhook not found")
        } else {
            respondError(w, http.StatusInternalServerError, err.Error())
        }
        return
    }
    if err := db.Delete(&wb).Error; err != nil {
        respondError(w, http.StatusInternalServerError, err.Error())
        return
    }
    respondJSON(w, http.StatusNoContent, nil)
}

// GetPeerConfig downloads the WireGuard configuration file for a peer.
// @Summary Get peer config
// @Description Downloads the WireGuard .conf file for a specific peer.
// @Tags peers
// @Produce plain
// @Param id path string true "Peer ID"
// @Success 200 {string} string "WireGuard configuration file"
// @Failure 404 {object} map[string]string
// @Failure 500 {object} map[string]string
// @Security ApiKeyAuth
// @Security BearerAuth
// @Router /api/peers/{id}/config [get]
func getPeerConfig(w http.ResponseWriter, r *http.Request) {
    vars := mux.Vars(r)
    id := vars["id"]
    var p Peer
    if err := db.First(&p, "id = ?", id).Error; err != nil {
        if err == gorm.ErrRecordNotFound {
            respondError(w, http.StatusNotFound, "peer not found")
        } else {
            respondError(w, http.StatusInternalServerError, err.Error())
        }
        return
    }
    var s Server
    if err := db.First(&s, "id = ?", p.ServerID).Error; err != nil {
        if err == gorm.ErrRecordNotFound {
            respondError(w, http.StatusNotFound, "server not found for peer")
        } else {
            respondError(w, http.StatusInternalServerError, err.Error())
        }
        return
    }
    conf, err := GeneratePeerConfig(p, s)
    if err != nil {
        respondError(w, http.StatusInternalServerError, err.Error())
        return
    }
    w.Header().Set("Content-Type", "text/plain")
    w.Header().Set("Content-Disposition", fmt.Sprintf("attachment; filename=peer-%d.conf", p.ID))
    w.Write(conf)
}

// GetPeerQRCode returns a QR code PNG image for the peer configuration.
// @Summary Get peer QR code
// @Description Returns a QR code PNG image of the peer config for mobile import.
// @Tags peers
// @Produce png
// @Param id path string true "Peer ID"
// @Success 200 {file} binary "QR code PNG image"
// @Failure 404 {object} map[string]string
// @Failure 500 {object} map[string]string
// @Security ApiKeyAuth
// @Security BearerAuth
// @Router /api/peers/{id}/qrcode [get]
func getPeerQRCode(w http.ResponseWriter, r *http.Request) {
    vars := mux.Vars(r)
    id := vars["id"]
    var p Peer
    if err := db.First(&p, "id = ?", id).Error; err != nil {
        if err == gorm.ErrRecordNotFound {
            respondError(w, http.StatusNotFound, "peer not found")
        } else {
            respondError(w, http.StatusInternalServerError, err.Error())
        }
        return
    }
    var s Server
    if err := db.First(&s, "id = ?", p.ServerID).Error; err != nil {
        if err == gorm.ErrRecordNotFound {
            respondError(w, http.StatusNotFound, "server not found for peer")
        } else {
            respondError(w, http.StatusInternalServerError, err.Error())
        }
        return
    }
    conf, err := GeneratePeerConfig(p, s)
    if err != nil {
        respondError(w, http.StatusInternalServerError, err.Error())
        return
    }
    png, err := qrcode.Encode(string(conf), qrcode.Medium, 256)
    if err != nil {
        respondError(w, http.StatusInternalServerError, err.Error())
        return
    }
    w.Header().Set("Content-Type", "image/png")
    w.Write(png)
}

// GetStats returns global statistics for servers, peers, and webhooks.
// @Summary Get global stats
// @Description Returns global statistics including total servers, peers, and webhooks.
// @Tags stats
// @Produce json
// @Success 200 {object} map[string]int64
// @Security ApiKeyAuth
// @Security BearerAuth
// @Router /api/stats [get]
func getStats(w http.ResponseWriter, r *http.Request) {
    var serverCount int64
    var peerCount int64
    var webhookCount int64
    db.Model(&Server{}).Count(&serverCount)
    db.Model(&Peer{}).Count(&peerCount)
    db.Model(&Webhook{}).Count(&webhookCount)
    resp := map[string]interface{}{
        "servers":  serverCount,
        "peers":    peerCount,
        "webhooks": webhookCount,
    }
    respondJSON(w, http.StatusOK, resp)
}

// GetServerStats returns per-server statistics.
// @Summary Get server stats
// @Description Returns per-server statistics including peer count and webhook count.
// @Tags stats
// @Produce json
// @Param id path string true "Server ID"
// @Success 200 {object} map[string]interface{}
// @Failure 404 {object} map[string]string
// @Failure 500 {object} map[string]string
// @Security ApiKeyAuth
// @Security BearerAuth
// @Router /api/servers/{id}/stats [get]
func getServerStats(w http.ResponseWriter, r *http.Request) {
    vars := mux.Vars(r)
    id := vars["id"]
    var s Server
    if err := db.First(&s, "id = ?", id).Error; err != nil {
        if err == gorm.ErrRecordNotFound {
            respondError(w, http.StatusNotFound, "server not found")
        } else {
            respondError(w, http.StatusInternalServerError, err.Error())
        }
        return
    }
    var peers []Peer
    var webhooks []Webhook
    db.Where("server_id = ?", id).Find(&peers)
    db.Where("server_id = ?", id).Find(&webhooks)
    resp := map[string]interface{}{
        "server":   s,
        "peers":    len(peers),
        "webhooks": len(webhooks),
    }
    respondJSON(w, http.StatusOK, resp)
}
