package setting

import (
	"fmt"
	"net/http"
	"time"

	"github.com/gin-gonic/gin"
	gouStore "github.com/yaoapp/gou/store"
	"github.com/yaoapp/kun/log"
	oauth "github.com/yaoapp/yao/openapi/oauth"
	"github.com/yaoapp/yao/openapi/oauth/authorized"
	oauthTypes "github.com/yaoapp/yao/openapi/oauth/types"
	"github.com/yaoapp/yao/openapi/response"
)

const ownerCachePrefix = "setting:owner:"
const ownerCacheTTL = 5 * time.Minute

func getCache() gouStore.Store {
	c, _ := gouStore.Get("__yao.cache")
	return c
}

// Attach registers all /setting/* routes under the given group.
// Currently only System Info routes are wired; other groups will be
// added incrementally.
func Attach(group *gin.RouterGroup, oauth oauthTypes.OAuth) {
	group.Use(oauth.Guard)

	sys := group.Group("/system")
	sys.GET("", handleSystemInfo)
	sys.POST("/check-update", handleSystemCheckUpdate)

	cloud := group.Group("/cloud")
	cloud.GET("", handleCloudGet)
	cloud.PUT("", handleCloudUpdate)
	cloud.POST("/test", handleCloudTest)

	llm := group.Group("/llm")
	llm.GET("", handleLLMGet)
	llm.PUT("/roles", handleLLMRoles)
	llm.POST("/test", handleLLMTest)
	llm.POST("/providers", handleLLMProviderCreate)
	llm.PUT("/providers/:key", handleLLMProviderUpdate)
	llm.DELETE("/providers/:key", handleLLMProviderDelete)
	llm.POST("/providers/:key/test", handleLLMProviderTest)

	search := group.Group("/search")
	search.GET("", handleSearchGet)
	search.PUT("/providers/:key", handleSearchProviderUpdate)
	search.PUT("/providers/:key/toggle", handleSearchProviderToggle)
	search.POST("/providers/:key/test", handleSearchProviderTest)
	search.PUT("/tool-assignment", handleSearchToolAssignment)

	smtpG := group.Group("/smtp")
	smtpG.GET("", handleSmtpGet)
	smtpG.PUT("", handleSmtpUpdate)
	smtpG.PUT("/toggle", handleSmtpToggle)
	smtpG.POST("/test", handleSmtpTest)

	mcpG := group.Group("/mcp")
	mcpG.GET("/servers", handleMCPList)
	mcpG.POST("/servers", handleMCPCreate)
	mcpG.PUT("/servers/:id", handleMCPUpdate)
	mcpG.DELETE("/servers/:id", handleMCPDelete)
	mcpG.POST("/test", handleMCPTest)

	sb := group.Group("/sandbox")
	sb.GET("", handleSandboxGet)
	sb.PUT("/registry", handleSandboxRegistry)
	sb.POST("/nodes/:nodeId/images/:imageId/pull", handleSandboxPull)
	sb.POST("/nodes/:nodeId/images/pull-all", handleSandboxPullAll)
	sb.DELETE("/nodes/:nodeId/images/:imageId", handleSandboxImageDelete)
	sb.POST("/nodes/:nodeId/check-docker", handleSandboxCheckDocker)
}

// requireOwner checks that the current user is the team owner.
// Non-team context (TeamID == ""): always allowed — user is managing their own data.
// Team context: checks cache first, then queries the member table is_owner field.
// Use this as a guard for any write operation across all /setting/* groups.
func requireOwner(c *gin.Context, info *oauthTypes.AuthorizedInfo) error {
	if info == nil || info.UserID == "" {
		return fmt.Errorf("authentication required")
	}
	if info.TeamID == "" {
		return nil
	}

	cacheKey := ownerCachePrefix + info.TeamID + ":" + info.UserID

	if cache := getCache(); cache != nil {
		if val, ok := cache.Get(cacheKey); ok {
			if isOwner, ok := val.(bool); ok {
				if isOwner {
					return nil
				}
				return fmt.Errorf("access denied: only team owner can modify settings")
			}
		}
	}

	if oauth.OAuth == nil {
		return fmt.Errorf("service not initialized")
	}
	provider, err := oauth.OAuth.GetUserProvider()
	if err != nil {
		return fmt.Errorf("service not available")
	}

	member, err := provider.GetMember(c.Request.Context(), info.TeamID, info.UserID)
	if err != nil {
		log.Error("[setting] GetMember failed: %v", err)
		return fmt.Errorf("access denied")
	}

	isOwner := checkIsOwner(member["is_owner"])
	if cache := getCache(); cache != nil {
		cache.Set(cacheKey, isOwner, ownerCacheTTL)
	}

	if isOwner {
		return nil
	}
	return fmt.Errorf("access denied: only team owner can modify settings")
}

func checkIsOwner(val interface{}) bool {
	switch v := val.(type) {
	case bool:
		return v
	case int:
		return v == 1
	case int64:
		return v == 1
	case float64:
		return v == 1
	}
	return false
}

// guardOwner is a convenience wrapper: calls requireOwner and writes 403 on failure.
// Returns true if the request should continue, false if it was aborted.
func guardOwner(c *gin.Context) bool {
	info := authorized.GetInfo(c)
	if err := requireOwner(c, info); err != nil {
		respondError(c, http.StatusForbidden, err.Error())
		return false
	}
	return true
}

// respondError is a thin helper that writes a JSON error via the shared
// response package.
func respondError(c *gin.Context, status int, msg string) {
	response.RespondWithError(c, status, &response.ErrorResponse{
		Code:             "server_error",
		ErrorDescription: msg,
	})
}
