package handler

import (
	"crypto/subtle"
	"errors"
	"net/http"
	"strings"

	"github.com/gin-gonic/gin"
	"gorm.io/gorm"

	"infogenie-backend/config"
	"infogenie-backend/internal/database"
	"infogenie-backend/internal/model"
)

type SiteConfigHandler struct{}

func NewSiteConfigHandler() *SiteConfigHandler { return &SiteConfigHandler{} }

func siteAdminTokenOK(headerToken string) bool {
	if config.Cfg == nil {
		return false
	}
	expected := strings.TrimSpace(config.Cfg.SiteAdminToken)
	if expected == "" {
		return false
	}
	return subtle.ConstantTimeCompare([]byte(headerToken), []byte(expected)) == 1
}

// Get60sDisabled 公开：返回当前隐藏的 60s 功能 id 列表（与前端 item.id 对应）
func (h *SiteConfigHandler) Get60sDisabled(c *gin.Context) {
	var rows []model.Site60sDisabled
	if err := database.DB.Order("feature_id").Find(&rows).Error; err != nil {
		c.JSON(http.StatusInternalServerError, gin.H{"error": "db_error"})
		return
	}
	ids := make([]string, 0, len(rows))
	for _, r := range rows {
		ids = append(ids, r.FeatureID)
	}
	c.JSON(http.StatusOK, gin.H{"disabled": ids})
}

type put60sDisabledBody struct {
	Disabled []string `json:"disabled"`
}

// Put60sDisabled 需请求头 X-Site-Admin-Token，与后端环境变量 INFOGENIE_SITE_ADMIN_TOKEN 一致（建议与前端管理员口令相同）
func (h *SiteConfigHandler) Put60sDisabled(c *gin.Context) {
	if config.Cfg == nil || strings.TrimSpace(config.Cfg.SiteAdminToken) == "" {
		c.JSON(http.StatusServiceUnavailable, gin.H{
			"error":   "admin_not_configured",
			"message": "服务端未配置 INFOGENIE_SITE_ADMIN_TOKEN，禁止写入",
		})
		return
	}
	if !siteAdminTokenOK(c.GetHeader("X-Site-Admin-Token")) {
		c.JSON(http.StatusForbidden, gin.H{"error": "forbidden", "message": "站点管理员令牌无效"})
		return
	}

	var body put60sDisabledBody
	if err := c.ShouldBindJSON(&body); err != nil {
		c.JSON(http.StatusBadRequest, gin.H{"error": "invalid_json"})
		return
	}
	if len(body.Disabled) > 512 {
		c.JSON(http.StatusBadRequest, gin.H{"error": "too_many"})
		return
	}

	seen := make(map[string]struct{})
	clean := make([]string, 0, len(body.Disabled))
	for _, raw := range body.Disabled {
		id := strings.TrimSpace(raw)
		if id == "" || len(id) > 96 {
			continue
		}
		if _, ok := seen[id]; ok {
			continue
		}
		seen[id] = struct{}{}
		clean = append(clean, id)
	}

	tx := database.DB.Begin()
	if err := tx.Session(&gorm.Session{AllowGlobalUpdate: true}).Delete(&model.Site60sDisabled{}).Error; err != nil {
		tx.Rollback()
		c.JSON(http.StatusInternalServerError, gin.H{"error": "db_error"})
		return
	}
	for _, id := range clean {
		if err := tx.Create(&model.Site60sDisabled{FeatureID: id}).Error; err != nil {
			tx.Rollback()
			c.JSON(http.StatusInternalServerError, gin.H{"error": "db_error"})
			return
		}
	}
	if err := tx.Commit().Error; err != nil {
		c.JSON(http.StatusInternalServerError, gin.H{"error": "db_error"})
		return
	}
	c.JSON(http.StatusOK, gin.H{"ok": true, "count": len(clean)})
}

// —— 60s API 上游节点（仅管理员可切换）——

type sixtySrcInfo struct {
	Base  string
	Label string
}

var sixtyUpstreamRegistry = map[string]sixtySrcInfo{
	"self":     {Base: "https://60s.api.shumengya.top", Label: "萌芽节点"},
	"official": {Base: "https://60s.viki.moe", Label: "官方节点"},
}

func resolve60sUpstream(sourceID string) (id string, info sixtySrcInfo) {
	id = strings.TrimSpace(sourceID)
	if id == "" {
		id = "self"
	}
	var ok bool
	info, ok = sixtyUpstreamRegistry[id]
	if !ok {
		id = "self"
		info = sixtyUpstreamRegistry["self"]
	}
	return id, info
}

// EffectiveSixtyUpstream 返回当前站点配置的 60s API 根地址（读库；无记录或库不可用则回落默认 self）
func EffectiveSixtyUpstream(db *gorm.DB) (sourceID string, base string, label string) {
	sid := ""
	if db != nil {
		var row model.Site60sUpstream
		if err := db.First(&row, 1).Error; err == nil {
			sid = row.SourceID
		}
	}
	id, info := resolve60sUpstream(sid)
	return id, info.Base, info.Label
}

// Get60sSource 公开：当前站点使用的 60s 上游 base_url（供静态页 iframe 传参）
func (h *SiteConfigHandler) Get60sSource(c *gin.Context) {
	var row model.Site60sUpstream
	_ = database.DB.First(&row, 1).Error
	sid, info := resolve60sUpstream(row.SourceID)
	c.JSON(http.StatusOK, gin.H{
		"source_id": sid,
		"base_url":  info.Base,
		"label":     info.Label,
	})
}

type put60sSourceBody struct {
	SourceID string `json:"source_id"`
}

// Put60sSource 管理员切换节点：source_id 为 self | official
func (h *SiteConfigHandler) Put60sSource(c *gin.Context) {
	if config.Cfg == nil || strings.TrimSpace(config.Cfg.SiteAdminToken) == "" {
		c.JSON(http.StatusServiceUnavailable, gin.H{"error": "admin_not_configured"})
		return
	}
	if !siteAdminTokenOK(c.GetHeader("X-Site-Admin-Token")) {
		c.JSON(http.StatusForbidden, gin.H{"error": "forbidden"})
		return
	}
	var body put60sSourceBody
	if err := c.ShouldBindJSON(&body); err != nil {
		c.JSON(http.StatusBadRequest, gin.H{"error": "invalid_json"})
		return
	}
	sid := strings.TrimSpace(body.SourceID)
	if _, ok := sixtyUpstreamRegistry[sid]; !ok {
		c.JSON(http.StatusBadRequest, gin.H{"error": "invalid_source_id"})
		return
	}
	var row model.Site60sUpstream
	err := database.DB.First(&row, 1).Error
	if err != nil {
		if errors.Is(err, gorm.ErrRecordNotFound) {
			row = model.Site60sUpstream{ID: 1}
		} else {
			c.JSON(http.StatusInternalServerError, gin.H{"error": "db_error"})
			return
		}
	}
	row.SourceID = sid
	if err := database.DB.Save(&row).Error; err != nil {
		c.JSON(http.StatusInternalServerError, gin.H{"error": "db_error"})
		return
	}
	_, info := resolve60sUpstream(sid)
	c.JSON(http.StatusOK, gin.H{"ok": true, "source_id": sid, "base_url": info.Base, "label": info.Label})
}

// —— AI 应用可见性控制（仅管理员可配置）——

// GetAIModelDisabled 公开：返回当前隐藏的 AI 应用 id 列表（与前端 StaticPageConfig 中 AI_MODEL_APPS 的索引对应）
func (h *SiteConfigHandler) GetAIModelDisabled(c *gin.Context) {
	var rows []model.SiteAIModelDisabled
	if err := database.DB.Order("app_id").Find(&rows).Error; err != nil {
		c.JSON(http.StatusInternalServerError, gin.H{"error": "db_error"})
		return
	}
	ids := make([]string, 0, len(rows))
	for _, r := range rows {
		ids = append(ids, r.AppID)
	}
	c.JSON(http.StatusOK, gin.H{"disabled": ids})
}

type putAIModelDisabledBody struct {
	Disabled []string `json:"disabled"`
}

// PutAIModelDisabled 需请求头 X-Site-Admin-Token，与后端环境变量 INFOGENIE_SITE_ADMIN_TOKEN 一致
func (h *SiteConfigHandler) PutAIModelDisabled(c *gin.Context) {
	if config.Cfg == nil || strings.TrimSpace(config.Cfg.SiteAdminToken) == "" {
		c.JSON(http.StatusServiceUnavailable, gin.H{
			"error":   "admin_not_configured",
			"message": "服务端未配置 INFOGENIE_SITE_ADMIN_TOKEN，禁止写入",
		})
		return
	}
	if !siteAdminTokenOK(c.GetHeader("X-Site-Admin-Token")) {
		c.JSON(http.StatusForbidden, gin.H{"error": "forbidden", "message": "站点管理员令牌无效"})
		return
	}

	var body putAIModelDisabledBody
	if err := c.ShouldBindJSON(&body); err != nil {
		c.JSON(http.StatusBadRequest, gin.H{"error": "invalid_json"})
		return
	}
	if len(body.Disabled) > 64 {
		c.JSON(http.StatusBadRequest, gin.H{"error": "too_many"})
		return
	}

	seen := make(map[string]struct{})
	clean := make([]string, 0, len(body.Disabled))
	for _, raw := range body.Disabled {
		id := strings.TrimSpace(raw)
		if id == "" || len(id) > 96 {
			continue
		}
		if _, ok := seen[id]; ok {
			continue
		}
		seen[id] = struct{}{}
		clean = append(clean, id)
	}

	tx := database.DB.Begin()
	if err := tx.Session(&gorm.Session{AllowGlobalUpdate: true}).Delete(&model.SiteAIModelDisabled{}).Error; err != nil {
		tx.Rollback()
		c.JSON(http.StatusInternalServerError, gin.H{"error": "db_error"})
		return
	}
	for _, id := range clean {
		if err := tx.Create(&model.SiteAIModelDisabled{AppID: id}).Error; err != nil {
			tx.Rollback()
			c.JSON(http.StatusInternalServerError, gin.H{"error": "db_error"})
			return
		}
	}
	if err := tx.Commit().Error; err != nil {
		c.JSON(http.StatusInternalServerError, gin.H{"error": "db_error"})
		return
	}
	c.JSON(http.StatusOK, gin.H{"ok": true, "count": len(clean)})
}
