package handler

import (
	"net/http"
	"strconv"
	"strings"

	"cyberstrike-ai/internal/database"
	"cyberstrike-ai/internal/project"

	"github.com/gin-gonic/gin"
	"go.uber.org/zap"
)

// ProjectHandler 项目管理处理器。
type ProjectHandler struct {
	db     *database.DB
	logger *zap.Logger
}

// NewProjectHandler 创建项目管理处理器。
func NewProjectHandler(db *database.DB, logger *zap.Logger) *ProjectHandler {
	return &ProjectHandler{db: db, logger: logger}
}

type createProjectRequest struct {
	Name        string `json:"name" binding:"required"`
	Description string `json:"description"`
	ScopeJSON   string `json:"scope_json"`
	Status      string `json:"status"`
}

// updateProjectRequest 部分更新：字段省略表示不修改；传 null 或 "" 可清空字符串字段。
type updateProjectRequest struct {
	Name        *string `json:"name"`
	Description *string `json:"description"`
	ScopeJSON   *string `json:"scope_json"`
	Status      *string `json:"status"`
	Pinned      *bool   `json:"pinned"`
}

// CreateProject POST /api/projects
func (h *ProjectHandler) CreateProject(c *gin.Context) {
	var req createProjectRequest
	if err := c.ShouldBindJSON(&req); err != nil {
		c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
		return
	}
	p := &database.Project{
		Name:        strings.TrimSpace(req.Name),
		Description: req.Description,
		ScopeJSON:   req.ScopeJSON,
		Status:      strings.TrimSpace(req.Status),
	}
	created, err := h.db.CreateProject(p)
	if err != nil {
		h.logger.Error("创建项目失败", zap.Error(err))
		c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
		return
	}
	c.JSON(http.StatusOK, created)
}

// ListProjects GET /api/projects
func (h *ProjectHandler) ListProjects(c *gin.Context) {
	status := c.Query("status")
	limit, _ := strconv.Atoi(c.DefaultQuery("limit", "200"))
	offset, _ := strconv.Atoi(c.Query("offset"))
	list, err := h.db.ListProjects(status, limit, offset)
	if err != nil {
		c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
		return
	}
	if list == nil {
		list = []*database.Project{}
	}
	c.JSON(http.StatusOK, list)
}

// GetProjectStats GET /api/projects/:id/stats
func (h *ProjectHandler) GetProjectStats(c *gin.Context) {
	stats, err := project.GetProjectStats(h.db, c.Param("id"))
	if err != nil {
		if strings.Contains(err.Error(), "不存在") {
			c.JSON(http.StatusNotFound, gin.H{"error": "项目不存在"})
			return
		}
		c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
		return
	}
	c.JSON(http.StatusOK, stats)
}

// ListProjectConversations GET /api/projects/:id/conversations
func (h *ProjectHandler) ListProjectConversations(c *gin.Context) {
	projectID := c.Param("id")
	if _, err := h.db.GetProject(projectID); err != nil {
		c.JSON(http.StatusNotFound, gin.H{"error": "项目不存在"})
		return
	}
	limit, _ := strconv.Atoi(c.DefaultQuery("limit", "100"))
	offset, _ := strconv.Atoi(c.Query("offset"))
	list, err := h.db.ListConversationsByProjectID(projectID, limit, offset)
	if err != nil {
		c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
		return
	}
	if list == nil {
		list = []*database.Conversation{}
	}
	total, _ := h.db.CountConversationsByProjectID(projectID)
	c.JSON(http.StatusOK, gin.H{
		"conversations": list,
		"total":         total,
		"limit":         limit,
		"offset":        offset,
	})
}

// GetProject GET /api/projects/:id
func (h *ProjectHandler) GetProject(c *gin.Context) {
	p, err := h.db.GetProject(c.Param("id"))
	if err != nil {
		c.JSON(http.StatusNotFound, gin.H{"error": "项目不存在"})
		return
	}
	c.JSON(http.StatusOK, p)
}

// UpdateProject PUT /api/projects/:id
func (h *ProjectHandler) UpdateProject(c *gin.Context) {
	id := c.Param("id")
	p, err := h.db.GetProject(id)
	if err != nil {
		c.JSON(http.StatusNotFound, gin.H{"error": "项目不存在"})
		return
	}
	var req updateProjectRequest
	if err := c.ShouldBindJSON(&req); err != nil {
		c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
		return
	}
	if req.Name != nil {
		if s := strings.TrimSpace(*req.Name); s != "" {
			p.Name = s
		}
	}
	if req.Description != nil {
		p.Description = *req.Description
	}
	if req.ScopeJSON != nil {
		p.ScopeJSON = *req.ScopeJSON
	}
	if req.Status != nil {
		if s := strings.TrimSpace(*req.Status); s != "" {
			p.Status = s
		}
	}
	if req.Pinned != nil {
		p.Pinned = *req.Pinned
	}
	if err := h.db.UpdateProject(p); err != nil {
		c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
		return
	}
	c.JSON(http.StatusOK, p)
}

// DeleteProject DELETE /api/projects/:id
func (h *ProjectHandler) DeleteProject(c *gin.Context) {
	if err := h.db.DeleteProject(c.Param("id")); err != nil {
		c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
		return
	}
	c.JSON(http.StatusOK, gin.H{"success": true})
}

type upsertFactRequest struct {
	FactKey                string `json:"fact_key" binding:"required"`
	Category               string `json:"category"`
	Summary                string `json:"summary" binding:"required"`
	Body                   string `json:"body"`
	Confidence             string `json:"confidence"`
	Pinned                 bool   `json:"pinned"`
	RelatedVulnerabilityID string `json:"related_vulnerability_id"`
}

// updateFactRequest 部分更新事实；指针字段省略=不修改，body 传 "" 可清空（仍走 merge 逻辑见 Upsert）。
type updateFactRequest struct {
	FactKey                *string `json:"fact_key"`
	Category               *string `json:"category"`
	Summary                *string `json:"summary"`
	Body                   *string `json:"body"`
	Confidence             *string `json:"confidence"`
	Pinned                 *bool   `json:"pinned"`
	RelatedVulnerabilityID *string `json:"related_vulnerability_id"`
	ClearBody              bool    `json:"clear_body"`
}

// ListFacts GET /api/projects/:id/facts （fact_key 查询参数可获取单条详情）
func (h *ProjectHandler) ListFacts(c *gin.Context) {
	projectID := c.Param("id")
	if key := strings.TrimSpace(c.Query("fact_key")); key != "" {
		f, err := h.db.GetProjectFactByKey(projectID, key)
		if err != nil {
			c.JSON(http.StatusNotFound, gin.H{"error": err.Error()})
			return
		}
		c.JSON(http.StatusOK, f)
		return
	}
	limit, _ := strconv.Atoi(c.DefaultQuery("limit", "100"))
	offset, _ := strconv.Atoi(c.Query("offset"))
	filter := database.ProjectFactListFilter{
		Category:               c.Query("category"),
		Confidence:             c.Query("confidence"),
		Search:                 c.Query("search"),
		RelatedVulnerabilityID: c.Query("related_vulnerability_id"),
	}
	if c.Query("exclude_deprecated") == "1" || c.Query("exclude_deprecated") == "true" {
		filter.ExcludeDeprecated = true
	}
	list, err := h.db.ListProjectFacts(projectID, filter, limit, offset)
	if err != nil {
		c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
		return
	}
	if list == nil {
		list = []*database.ProjectFact{}
	}
	if sparseOnly := c.Query("sparse_only"); sparseOnly == "1" || sparseOnly == "true" {
		filtered := make([]*database.ProjectFact, 0, len(list))
		for _, f := range list {
			if project.IsSparseFactBody(f.Category, f.FactKey, f.Body) {
				filtered = append(filtered, f)
			}
		}
		list = filtered
	}
	c.JSON(http.StatusOK, list)
}

// GetFactPreviousVersion GET /api/projects/:id/facts/:factId/previous-version
func (h *ProjectHandler) GetFactPreviousVersion(c *gin.Context) {
	existing, err := h.db.GetProjectFact(c.Param("factId"))
	if err != nil || existing.ProjectID != c.Param("id") {
		c.JSON(http.StatusNotFound, gin.H{"error": "事实不存在"})
		return
	}
	if strings.TrimSpace(existing.SupersedesFactID) == "" {
		c.JSON(http.StatusNotFound, gin.H{"error": "无上一版本"})
		return
	}
	v, err := h.db.GetProjectFactVersion(existing.SupersedesFactID)
	if err != nil {
		c.JSON(http.StatusNotFound, gin.H{"error": err.Error()})
		return
	}
	c.JSON(http.StatusOK, v)
}

// ListFactVersions GET /api/projects/:id/facts/:factId/versions
func (h *ProjectHandler) ListFactVersions(c *gin.Context) {
	existing, err := h.db.GetProjectFact(c.Param("factId"))
	if err != nil || existing.ProjectID != c.Param("id") {
		c.JSON(http.StatusNotFound, gin.H{"error": "事实不存在"})
		return
	}
	limit, _ := strconv.Atoi(c.DefaultQuery("limit", "20"))
	list, err := h.db.ListProjectFactVersions(existing.ID, limit)
	if err != nil {
		c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
		return
	}
	if list == nil {
		list = []*database.ProjectFactVersion{}
	}
	c.JSON(http.StatusOK, list)
}

// CreateFact POST /api/projects/:id/facts
func (h *ProjectHandler) CreateFact(c *gin.Context) {
	var req upsertFactRequest
	if err := c.ShouldBindJSON(&req); err != nil {
		c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
		return
	}
	f := &database.ProjectFact{
		ProjectID:              c.Param("id"),
		FactKey:                req.FactKey,
		Category:               req.Category,
		Summary:                req.Summary,
		Body:                   req.Body,
		Confidence:             req.Confidence,
		Pinned:                 req.Pinned,
		RelatedVulnerabilityID: req.RelatedVulnerabilityID,
	}
	created, err := h.db.UpsertProjectFact(f)
	if err != nil {
		c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
		return
	}
	c.JSON(http.StatusOK, created)
}

// UpdateFact PUT /api/projects/:id/facts/:factId
func (h *ProjectHandler) UpdateFact(c *gin.Context) {
	existing, err := h.db.GetProjectFact(c.Param("factId"))
	if err != nil || existing.ProjectID != c.Param("id") {
		c.JSON(http.StatusNotFound, gin.H{"error": "事实不存在"})
		return
	}
	var req updateFactRequest
	if err := c.ShouldBindJSON(&req); err != nil {
		c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
		return
	}
	if req.FactKey != nil {
		if k := strings.TrimSpace(*req.FactKey); k != "" {
			existing.FactKey = k
		}
	}
	if req.Category != nil && strings.TrimSpace(*req.Category) != "" {
		existing.Category = *req.Category
	}
	if req.Summary != nil && strings.TrimSpace(*req.Summary) != "" {
		existing.Summary = *req.Summary
	}
	if req.ClearBody {
		existing.Body = ""
	} else if req.Body != nil {
		existing.Body = *req.Body
	}
	if req.Confidence != nil && strings.TrimSpace(*req.Confidence) != "" {
		existing.Confidence = *req.Confidence
	}
	if req.Pinned != nil {
		existing.Pinned = *req.Pinned
	}
	if req.RelatedVulnerabilityID != nil {
		existing.RelatedVulnerabilityID = *req.RelatedVulnerabilityID
	}
	updated, err := h.db.UpsertProjectFact(existing)
	if err != nil {
		c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
		return
	}
	c.JSON(http.StatusOK, updated)
}

// DeleteFact DELETE /api/projects/:id/facts/:factId
func (h *ProjectHandler) DeleteFact(c *gin.Context) {
	existing, err := h.db.GetProjectFact(c.Param("factId"))
	if err != nil || existing.ProjectID != c.Param("id") {
		c.JSON(http.StatusNotFound, gin.H{"error": "事实不存在"})
		return
	}
	if err := h.db.DeleteProjectFact(existing.ID); err != nil {
		c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
		return
	}
	c.JSON(http.StatusOK, gin.H{"success": true})
}

type deprecateFactRequest struct {
	FactKey string `json:"fact_key" binding:"required"`
}

// DeprecateFact POST /api/projects/:id/facts/deprecate
func (h *ProjectHandler) DeprecateFact(c *gin.Context) {
	var req deprecateFactRequest
	if err := c.ShouldBindJSON(&req); err != nil {
		c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
		return
	}
	if err := h.db.DeprecateProjectFact(c.Param("id"), req.FactKey); err != nil {
		c.JSON(http.StatusNotFound, gin.H{"error": err.Error()})
		return
	}
	c.JSON(http.StatusOK, gin.H{"success": true})
}

type restoreFactRequest struct {
	FactKey    string `json:"fact_key" binding:"required"`
	Confidence string `json:"confidence"` // 可选：confirmed | tentative，默认 tentative
}

// RestoreFact POST /api/projects/:id/facts/restore
func (h *ProjectHandler) RestoreFact(c *gin.Context) {
	var req restoreFactRequest
	if err := c.ShouldBindJSON(&req); err != nil {
		c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
		return
	}
	if err := h.db.RestoreProjectFact(c.Param("id"), req.FactKey, req.Confidence); err != nil {
		c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
		return
	}
	c.JSON(http.StatusOK, gin.H{"success": true})
}
