package api

import (
	"net/http"
	"strconv"

	"github.com/gin-gonic/gin"
	"github.com/wormhole/wormhole/internal/auth"
	"github.com/wormhole/wormhole/internal/store"
)

func (s *Server) dashboardRankings(c *gin.Context) {
	rankType := c.DefaultQuery("type", "tunnels")
	page, _ := strconv.Atoi(c.DefaultQuery("page", "1"))
	pageSize, _ := strconv.Atoi(c.DefaultQuery("page_size", "50"))
	if page < 1 {
		page = 1
	}
	if pageSize < 1 {
		pageSize = 50
	}
	if pageSize > 500 {
		pageSize = 500
	}
	offset := (page - 1) * pageSize
	q := c.Query("q")

	switch rankType {
	case "tunnels":
		items, total, err := s.store.ListTunnelsByFlow(pageSize, offset)
		if err != nil {
			c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
			return
		}
		c.JSON(http.StatusOK, gin.H{"items": items, "total": total})
	case "hosts":
		items, total, err := s.store.ListHostsByFlow(pageSize, offset)
		if err != nil {
			c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
			return
		}
		c.JSON(http.StatusOK, gin.H{"items": items, "total": total})
	case "ips":
		items, total, err := s.store.ListAllRequestIPs(pageSize, offset, q)
		if err != nil {
			c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
			return
		}
		c.JSON(http.StatusOK, gin.H{"items": requestIPViews(s.store, items), "total": total})
	default:
		c.JSON(http.StatusBadRequest, gin.H{"error": "invalid type"})
	}
}

func (s *Server) listAPIKeys(c *gin.Context) {
	claims := getClaims(c)
	keys, err := s.store.ListAPIKeys(claims.UserID, auth.IsAdmin(claims.Role))
	if err != nil {
		c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
		return
	}
	c.JSON(http.StatusOK, keys)
}

func (s *Server) createAPIKey(c *gin.Context) {
	claims := getClaims(c)
	var req struct {
		Name string `json:"name"`
	}
	if err := c.ShouldBindJSON(&req); err != nil {
		c.JSON(http.StatusBadRequest, gin.H{"error": "bad request"})
		return
	}
	key, plain, err := s.store.CreateAPIKey(req.Name, claims.UserID)
	if err != nil {
		c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
		return
	}
	c.JSON(http.StatusOK, gin.H{
		"key":     key,
		"api_key": plain,
		"note":    "请妥善保存 API Key，仅显示一次",
	})
}

func (s *Server) deleteAPIKey(c *gin.Context) {
	claims := getClaims(c)
	id, _ := strconv.ParseUint(c.Param("id"), 10, 64)
	if err := s.store.DeleteAPIKey(uint(id), claims.UserID, auth.IsAdmin(claims.Role)); err != nil {
		c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
		return
	}
	c.JSON(http.StatusOK, gin.H{"ok": true})
}

func (s *Server) pauseTunnel(c *gin.Context) {
	id, _ := strconv.ParseUint(c.Param("id"), 10, 64)
	claims := getClaims(c)
	existing, err := s.store.GetTunnelByID(uint(id))
	if err != nil {
		c.JSON(http.StatusNotFound, gin.H{"error": "not found"})
		return
	}
	if err := store.ClientOwnerCheck(s.store, existing.ClientID, claims.UserID, auth.IsAdmin(claims.Role)); err != nil {
		c.JSON(http.StatusForbidden, gin.H{"error": err.Error()})
		return
	}
	_ = s.proxy.StopTunnel(uint(id))
	existing.Status = false
	existing.RunStatus = false
	_ = s.store.UpdateTunnel(existing)
	s.cache.UpsertTunnel(existing)
	c.JSON(http.StatusOK, gin.H{"ok": true})
}

func (s *Server) resumeTunnel(c *gin.Context) {
	id, _ := strconv.ParseUint(c.Param("id"), 10, 64)
	claims := getClaims(c)
	existing, err := s.store.GetTunnelByID(uint(id))
	if err != nil {
		c.JSON(http.StatusNotFound, gin.H{"error": "not found"})
		return
	}
	if err := store.ClientOwnerCheck(s.store, existing.ClientID, claims.UserID, auth.IsAdmin(claims.Role)); err != nil {
		c.JSON(http.StatusForbidden, gin.H{"error": err.Error()})
		return
	}
	existing.Status = true
	_ = s.store.UpdateTunnel(existing)
	s.cache.UpsertTunnel(existing)
	_ = s.proxy.StartTunnel(uint(id))
	c.JSON(http.StatusOK, gin.H{"ok": true})
}

func (s *Server) pauseHost(c *gin.Context) {
	id, _ := strconv.ParseUint(c.Param("id"), 10, 64)
	claims := getClaims(c)
	existing, err := s.store.GetHostByID(uint(id))
	if err != nil {
		c.JSON(http.StatusNotFound, gin.H{"error": "not found"})
		return
	}
	if err := store.ClientOwnerCheck(s.store, existing.ClientID, claims.UserID, auth.IsAdmin(claims.Role)); err != nil {
		c.JSON(http.StatusForbidden, gin.H{"error": err.Error()})
		return
	}
	existing.Status = false
	_ = s.store.UpdateHost(existing)
	s.cache.UpsertHost(existing)
	c.JSON(http.StatusOK, gin.H{"ok": true})
}

func (s *Server) resumeHost(c *gin.Context) {
	id, _ := strconv.ParseUint(c.Param("id"), 10, 64)
	claims := getClaims(c)
	existing, err := s.store.GetHostByID(uint(id))
	if err != nil {
		c.JSON(http.StatusNotFound, gin.H{"error": "not found"})
		return
	}
	if err := store.ClientOwnerCheck(s.store, existing.ClientID, claims.UserID, auth.IsAdmin(claims.Role)); err != nil {
		c.JSON(http.StatusForbidden, gin.H{"error": err.Error()})
		return
	}
	existing.Status = true
	_ = s.store.UpdateHost(existing)
	s.cache.UpsertHost(existing)
	c.JSON(http.StatusOK, gin.H{"ok": true})
}

func (s *Server) openapiDoc(c *gin.Context) {
	c.Header("Content-Type", "application/json")
	c.String(http.StatusOK, openAPISpec)
}
