package handler

import (
	"log"
	"net/http"
	"strings"

	"github.com/gin-gonic/gin"

	"github.com/sundynix/sundynix-gateway/internal/store"
	"github.com/sundynix/sundynix-shared/contract"
)

// SpacesList: GET /api/v1/me/spaces —— 当前活跃租户内我所属的工作区（供切换器）+ 当前活跃空间 id。
func (h *Handler) SpacesList(c *gin.Context) {
	rows, err := h.db.MySpaces(c.Request.Context(), tenantID(c), userID(c))
	if err != nil {
		c.JSON(http.StatusBadGateway, gin.H{"error": err.Error()})
		return
	}
	c.JSON(http.StatusOK, gin.H{"spaces": rows, "active": spaceID(c)})
}

// SwitchSpace: POST /api/v1/me/space {space_id} —— 切换当前活跃工作区（须为其成员）。
func (h *Handler) SwitchSpace(c *gin.Context) {
	var b struct {
		SpaceID string `json:"space_id"`
	}
	if err := c.ShouldBindJSON(&b); err != nil || b.SpaceID == "" {
		c.JSON(http.StatusBadRequest, gin.H{"error": "space_id 必填"})
		return
	}
	if err := h.db.SetActiveSpace(c.Request.Context(), userID(c), b.SpaceID); err != nil {
		c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
		return
	}
	c.JSON(http.StatusOK, gin.H{"status": "ok", "active": b.SpaceID})
}

// SpaceCurrent: GET /api/v1/spaces/current —— 当前活跃工作区上下文 + 我在其中的角色。
func (h *Handler) SpaceCurrent(c *gin.Context) {
	sid := spaceID(c)
	if sid == "" {
		c.JSON(http.StatusOK, gin.H{"space": nil})
		return
	}
	s, _ := h.db.GetSpace(c.Request.Context(), sid)
	if s == nil {
		c.JSON(http.StatusOK, gin.H{"space": nil})
		return
	}
	c.JSON(http.StatusOK, gin.H{
		"space": gin.H{"id": s.ID, "name": s.Name, "kind": s.Kind, "creator": s.Creator, "archived": s.Archived},
		"role":  h.db.SpaceMemberRole(c.Request.Context(), sid, userID(c)),
	})
}

// SpaceCreate: POST /api/v1/spaces {name, kind} —— 在活跃租户建一个工作区（建者成为 owner）。
// 已由 RequireTenantRole(member) 门控：租户只读 viewer 进不来。
// kind=tenant（全员空间）走 EnableTenantSpace（幂等纳入全部成员），且须租户 admin。
func (h *Handler) SpaceCreate(c *gin.Context) {
	var b struct {
		Name string `json:"name"`
		Kind string `json:"kind"`
	}
	_ = c.ShouldBindJSON(&b)
	if b.Kind == store.SpaceKindTenant {
		// 启用全员空间：整租户级动作，须租户 admin/owner。
		if store.RoleRank(h.db.MemberRole(c.Request.Context(), tenantID(c), userID(c))) < store.RoleRank(store.RoleAdmin) {
			c.JSON(http.StatusForbidden, gin.H{"error": "启用全员空间需要租户管理员权限"})
			return
		}
		s, err := h.db.EnableTenantSpace(c.Request.Context(), tenantID(c))
		if err != nil {
			c.JSON(http.StatusBadGateway, gin.H{"error": err.Error()})
			return
		}
		c.JSON(http.StatusOK, gin.H{"id": s.ID, "name": s.Name, "kind": s.Kind})
		return
	}
	if strings.TrimSpace(b.Name) == "" {
		c.JSON(http.StatusBadRequest, gin.H{"error": "name 必填"})
		return
	}
	s, err := h.db.CreateSpace(c.Request.Context(), tenantID(c), b.Name, store.SpaceKindProject, userID(c))
	if err != nil {
		c.JSON(http.StatusBadGateway, gin.H{"error": err.Error()})
		return
	}
	c.JSON(http.StatusOK, gin.H{"id": s.ID, "name": s.Name, "kind": s.Kind})
}

// spaceAdminOr403 校验当前用户在指定空间是 admin/owner，否则写 403 并返回 false。
func (h *Handler) spaceAdminOr403(c *gin.Context, sid string) bool {
	if store.RoleRank(h.db.SpaceMemberRole(c.Request.Context(), sid, userID(c))) < store.RoleRank(store.RoleAdmin) {
		c.JSON(http.StatusForbidden, gin.H{"error": "需要空间管理员权限"})
		return false
	}
	return true
}

// SpaceMembers: GET /api/v1/spaces/:id/members —— 空间成员列表（须为该空间成员）。
func (h *Handler) SpaceMembers(c *gin.Context) {
	sid := c.Param("id")
	if h.db.SpaceMemberRole(c.Request.Context(), sid, userID(c)) == "" {
		c.JSON(http.StatusForbidden, gin.H{"error": "你不是该空间的成员"})
		return
	}
	rows, err := h.db.ListSpaceMembers(c.Request.Context(), sid)
	if err != nil {
		c.JSON(http.StatusBadGateway, gin.H{"error": err.Error()})
		return
	}
	c.JSON(http.StatusOK, gin.H{"members": rows})
}

// SpaceAddMember: POST /api/v1/spaces/:id/members {email, role} —— 拉租户成员进空间（须空间 admin）。
func (h *Handler) SpaceAddMember(c *gin.Context) {
	sid := c.Param("id")
	if !h.spaceAdminOr403(c, sid) {
		return
	}
	var b struct {
		Email string `json:"email"`
		Role  string `json:"role"`
	}
	if err := c.ShouldBindJSON(&b); err != nil || strings.TrimSpace(b.Email) == "" {
		c.JSON(http.StatusBadRequest, gin.H{"error": "email 必填"})
		return
	}
	if b.Role == "" {
		b.Role = store.RoleMember
	}
	m, err := h.db.AddSpaceMemberByEmail(c.Request.Context(), sid, b.Email, b.Role)
	if err != nil {
		c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
		return
	}
	c.JSON(http.StatusOK, gin.H{"member": m})
}

// SpaceSetMemberRole: PUT /api/v1/spaces/:id/members/:uid {role} —— 改空间成员角色（须空间 admin）。
func (h *Handler) SpaceSetMemberRole(c *gin.Context) {
	sid := c.Param("id")
	if !h.spaceAdminOr403(c, sid) {
		return
	}
	var b struct {
		Role string `json:"role"`
	}
	if err := c.ShouldBindJSON(&b); err != nil {
		c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
		return
	}
	if err := h.db.SetSpaceMemberRole(c.Request.Context(), sid, c.Param("uid"), b.Role); err != nil {
		c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
		return
	}
	c.JSON(http.StatusOK, gin.H{"status": "ok"})
}

// SpaceRemoveMember: DELETE /api/v1/spaces/:id/members/:uid —— 移除空间成员（须空间 admin）。
func (h *Handler) SpaceRemoveMember(c *gin.Context) {
	sid := c.Param("id")
	if !h.spaceAdminOr403(c, sid) {
		return
	}
	if err := h.db.RemoveSpaceMember(c.Request.Context(), sid, c.Param("uid")); err != nil {
		c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
		return
	}
	c.JSON(http.StatusOK, gin.H{"status": "ok"})
}

// MigrateKBStorage: POST /api/v1/admin/migrate-kb-storage —— 一次性把存量 KB 的三库(向量/全文/图谱)
// 从旧作用域键 owner/kb 重灌到新键 space_id/kb（增量3 存储层迁移）。RequireAdmin 保护。
// **异步**：为每篇文档在新 space 作用域下入队一条重灌作业（复用 JetStream 入库 worker 池：有界并发+
// 背压+崩溃重投），并先删旧键条目。立即返回入队数，进度经入库 worker 日志/SSE 看。避免同步重嵌几十篇撑爆 HTTP 超时。
func (h *Handler) MigrateKBStorage(c *gin.Context) {
	docs, err := h.db.DocsForRestorage(c.Request.Context())
	if err != nil {
		c.JSON(http.StatusBadGateway, gin.H{"error": err.Error()})
		return
	}
	enqueued, skipped := 0, 0
	for _, d := range docs {
		newScoped := d.SpaceID + "/" + d.KB
		oldScoped := d.Owner + "/" + d.KB
		if newScoped == oldScoped {
			skipped++
			continue
		}
		text := d.Content
		if d.ObjectKey != "" && h.blob.Ready() {
			if obj, oerr := h.blob.Get(c.Request.Context(), d.ObjectKey); oerr == nil {
				text = obj
			}
		}
		if strings.TrimSpace(text) == "" {
			skipped++
			continue
		}
		// 先删旧键条目(best-effort，幂等)。
		_, _ = h.bus.CallTool(c.Request.Context(), contract.ToolSubjectGo("kb_delete"),
			&contract.ToolCall{Tool: "kb_delete", Args: map[string]any{"kb": oldScoped, "file_id": d.ID}})
		// 入队新 space 作用域的重灌作业（forceDoc=原名保持文档身份；worker 走完整入库管线）。
		if _, e := h.enqueueIngest(c.Request.Context(), d.SpaceID, d.Owner, d.KB, newScoped, d.Name, "", nil, text); e != nil {
			log.Printf("[migrate-kb] 入队重灌失败 doc=%s: %v", d.ID, e)
			continue
		}
		enqueued++
	}
	c.JSON(http.StatusOK, gin.H{"total": len(docs), "enqueued": enqueued, "skipped": skipped})
}

// SpaceArchive: POST /api/v1/spaces/:id/archive —— 归档空间（须空间 admin；个人空间不可）。
func (h *Handler) SpaceArchive(c *gin.Context) {
	sid := c.Param("id")
	if !h.spaceAdminOr403(c, sid) {
		return
	}
	if err := h.db.ArchiveSpace(c.Request.Context(), sid); err != nil {
		c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
		return
	}
	c.JSON(http.StatusOK, gin.H{"status": "ok"})
}
