package handlers

import (
	"fmt"
	"log"
	"strconv"

	"github.com/gin-gonic/gin"
	"github.com/niangaodev/art-code/models"
	"github.com/niangaodev/art-code/repositories"
	"github.com/niangaodev/art-code/utils"
)

func trySavePostHistory(c *gin.Context, postID uint) bool {
	userID, _ := c.Get("userID")
	modifiedBy, ok := userID.(uint)
	if !ok {
		log.Printf("Error saving post history for post %d: invalid userID", postID)
		return false
	}
	saved, err := repositories.GetPostByIDAdmin(postID)
	if err != nil {
		log.Printf("Error saving post history for post %d: reload failed: %v", postID, err)
		return false
	}
	if saved == nil {
		log.Printf("Error saving post history for post %d: post not found after save", postID)
		return false
	}
	if err := repositories.SavePostHistory(saved, modifiedBy); err != nil {
		log.Printf("Error saving post history for post %d: %v", postID, err)
		return false
	}
	return true
}

// 获取博客文章列表 (前台)
func GetPosts(c *gin.Context) {
	// 获取查询参数
	keyword := c.Query("q")
	categoryIDStr := c.Query("category")
	tagIDStr := c.Query("tag")
	columnIDStr := c.Query("column")

	var categoryID uint
	if categoryIDStr != "" {
		if id, err := strconv.ParseUint(categoryIDStr, 10, 32); err == nil {
			categoryID = uint(id)
		}
	}

	var tagID uint
	if tagIDStr != "" {
		if id, err := strconv.ParseUint(tagIDStr, 10, 32); err == nil {
			tagID = uint(id)
		}
	}

	var columnID uint
	if columnIDStr != "" {
		if id, err := strconv.ParseUint(columnIDStr, 10, 32); err == nil {
			columnID = uint(id)
		}
	}

	// 获取分页参数
	page, _ := strconv.Atoi(c.DefaultQuery("page", "1"))
	if page < 1 {
		page = 1
	}

	// 从数据库读取 posts_per_page 配置作为默认值
	pageSize := 10 // 默认值
	setting, err := repositories.GetSettingByKey("posts_per_page")
	if err == nil && setting != nil {
		if ps, err := strconv.Atoi(setting.Value); err == nil && ps > 0 {
			pageSize = ps
		}
	}

	// 如果请求中指定了 pageSize，则使用请求的值
	if pageSizeStr := c.Query("pageSize"); pageSizeStr != "" {
		if ps, err := strconv.Atoi(pageSizeStr); err == nil && ps > 0 {
			pageSize = ps
		}
	}

	// 从数据库获取博客文章（支持分页）
	posts, total, err := repositories.GetPosts(keyword, categoryID, tagID, columnID, page, pageSize)
	if err != nil {
		utils.ServerError(c, err)
		return
	}

	// 异步记录搜索日志
	userIP := c.ClientIP()
	userLocation := utils.GetRegion(userIP)

	if keyword != "" {
		LogSearch(keyword, "keyword", userIP, userLocation)
	}
	if categoryID > 0 {
		LogSearch(strconv.FormatUint(uint64(categoryID), 10), "category", userIP, userLocation)
	}
	if tagID > 0 {
		LogSearch(strconv.FormatUint(uint64(tagID), 10), "tag", userIP, userLocation)
	}
	if columnID > 0 {
		LogSearch(strconv.FormatUint(uint64(columnID), 10), "column", userIP, userLocation)
	}

	// 构建响应（返回分页格式）
	responses := repositories.BuildPostsResponse(posts)
	if responses == nil {
		responses = []models.PostResponse{}
	}

	// 返回分页格式的响应
	res := gin.H{
		"list":  responses,
		"total": total,
		"page":  page,
		"size":  pageSize,
	}

	utils.Success(c, res)
}

func GetPost(c *gin.Context) {
	idStr := c.Param("id")
	// 转换ID
	var id uint
	if _, err := fmt.Sscanf(idStr, "%d", &id); err != nil {
		utils.Error(c, 400, "Invalid post ID")
		return
	}

	// 从数据库获取博客文章
	post, err := repositories.GetPostByID(id)
	if err != nil {
		utils.ServerError(c, err)
		return
	}

	if post == nil {
		// User requested empty object for details if not found?
		// "Details return empty object... otherwise frontend errors"
		// If I return 404, frontend api.ts might throw.
		// If I return 200 with empty result, frontend might handle it better if it checks result.
		// But empty object {} is safer than null.
		// Let's return error for now as it's more standard, but user asked for "empty object".
		// Actually, let's look at api.ts: `fetchPost` returns `Post` object.
		// If it gets null, it might crash access properties.
		// If it gets {}, it's fine (properties undefined).
		// But usually we want 404.
		// Let's stick to Error for Not Found, but ensure api.ts handles it or returns default object.
		// My api.ts update handles errors by returning default object for details!
		utils.Error(c, 404, "Post not found")
		return
	}

	// 构建响应，包含内容
	response := repositories.BuildPostResponse(post, true)

	visitorKey := utils.GetOrSetVisitorID(c)

	var userID uint = 0
	if uid, exists := c.Get("userID"); exists {
		userID = uid.(uint)
	}

	postID := post.ID
	clientIP := c.ClientIP()

	utils.Success(c, response)

	// 访问量统计与访问日志异步执行，不阻塞文章响应
	go func() {
		defer func() {
			if r := recover(); r != nil {
				log.Printf("Panic in visit stats goroutine for post %d: %v", postID, r)
			}
		}()

		alreadyVisited, err := repositories.HasVisitedThisHour(postID, userID, visitorKey)
		if err != nil {
			log.Printf("Failed to check visit dedup for post %d: %v", postID, err)
			return
		}
		if alreadyVisited {
			return
		}

		if err := repositories.IncrementReadCount(postID); err != nil {
			log.Printf("Failed to increment read count for post %d: %v", postID, err)
		}

		var location string
		func() {
			defer func() {
				if r := recover(); r != nil {
					log.Printf("Panic in GetRegion for IP %s (post %d): %v", clientIP, postID, r)
					location = "Unknown"
				}
			}()
			location = utils.GetRegion(clientIP)
		}()

		if location == "" {
			location = "Unknown"
		}

		logEntry := &models.UserAccessLog{
			UserID:       userID,
			UserIP:       clientIP,
			UserLocation: location,
			ArticleID:    postID,
			VisitorKey:   visitorKey,
		}

		if err := repositories.CreateUserAccessLog(logEntry); err != nil {
			log.Printf("Failed to create user access log for PostID=%d, IP=%s: %v", postID, clientIP, err)
		}
	}()
}

// 获取所有文章（包括未发布的，后台用）
func AdminGetPosts(c *gin.Context) {
	page, _ := strconv.Atoi(c.DefaultQuery("page", "1"))
	if page < 1 {
		page = 1
	}

	pageSize, _ := strconv.Atoi(c.DefaultQuery("pageSize", "20")) // 默认20条
	if pageSize < 1 {
		pageSize = 20
	}

	// 获取搜索与筛选参数
	filter := repositories.AdminPostFilter{
		Keyword: c.Query("keyword"),
	}
	if filter.Keyword == "" {
		filter.Keyword = c.Query("q")
	}
	if categoryIDStr := c.Query("categoryId"); categoryIDStr != "" {
		if id, err := strconv.ParseUint(categoryIDStr, 10, 32); err == nil {
			filter.CategoryID = uint(id)
		}
	}
	if columnIDStr := c.Query("columnId"); columnIDStr != "" {
		if id, err := strconv.ParseUint(columnIDStr, 10, 32); err == nil {
			filter.ColumnID = uint(id)
		}
	}
	if pubStr := c.Query("isPublished"); pubStr != "" {
		if v, err := strconv.Atoi(pubStr); err == nil {
			filter.IsPublished = v
		}
	} else {
		filter.IsPublished = -1
	}
	filter.StartDate = c.Query("startDate")
	filter.EndDate = c.Query("endDate")

	posts, total, err := repositories.GetAllPosts(page, pageSize, filter)
	if err != nil {
		utils.ServerError(c, err)
		return
	}

	list := repositories.BuildPostsResponse(posts)
	res := gin.H{
		"list":  list,
		"total": total,
		"page":  page,
		"size":  pageSize,
	}
	if list == nil {
		res["list"] = []interface{}{}
	}

	utils.Success(c, res)
}

// 创建文章
func AdminCreatePost(c *gin.Context) {
	var post models.Post
	if err := c.ShouldBindJSON(&post); err != nil {
		utils.Error(c, 400, "Invalid request")
		return
	}

	// 创建文章
	if userID, ok := c.Get("userID"); ok {
		if uid, ok := userID.(uint); ok {
			post.UserID = uid
		}
	}
	if err := repositories.CreatePost(&post); err != nil {
		utils.ServerError(c, err)
		return
	}

	historySaved := trySavePostHistory(c, post.ID)

	utils.SuccessWithMsg(c, "Post created successfully", gin.H{"id": post.ID, "historySaved": historySaved})
}

// 更新文章
func AdminUpdatePost(c *gin.Context) {
	postIDStr := c.Param("id")
	var postID uint
	if _, err := fmt.Sscanf(postIDStr, "%d", &postID); err != nil {
		utils.Error(c, 400, "Invalid post ID")
		return
	}

	var post models.Post
	if err := c.ShouldBindJSON(&post); err != nil {
		utils.Error(c, 400, "Invalid request")
		return
	}

	// 设置文章ID
	post.ID = postID

	// 更新文章
	if err := repositories.UpdatePost(&post); err != nil {
		utils.ServerError(c, err)
		return
	}

	historySaved := trySavePostHistory(c, postID)

	utils.SuccessWithMsg(c, "Post updated successfully", gin.H{"historySaved": historySaved})
}

// 更新文章关联关系（只更新分类、专栏、标签，不更新内容）
func AdminUpdatePostRelations(c *gin.Context) {
	postIDStr := c.Param("id")
	var postID uint
	if _, err := fmt.Sscanf(postIDStr, "%d", &postID); err != nil {
		utils.Error(c, 400, "Invalid post ID")
		return
	}

	var req struct {
		CategoryID uint   `json:"categoryId"`
		ColumnID   *uint  `json:"columnId"`
		TagIDs     []uint `json:"tagIds"`
	}
	if err := c.ShouldBindJSON(&req); err != nil {
		utils.Error(c, 400, "Invalid request")
		return
	}

	// 更新关联关系
	if err := repositories.UpdatePostRelations(postID, req.CategoryID, req.ColumnID, req.TagIDs); err != nil {
		utils.ServerError(c, err)
		return
	}

	historySaved := trySavePostHistory(c, postID)

	utils.SuccessWithMsg(c, "Post relations updated successfully", gin.H{"historySaved": historySaved})
}

// 切换文章发布状态
func AdminTogglePostStatus(c *gin.Context) {
	postIDStr := c.Param("id")
	var postID uint
	if _, err := fmt.Sscanf(postIDStr, "%d", &postID); err != nil {
		utils.Error(c, 400, "Invalid post ID")
		return
	}

	var req struct {
		IsPublished int `json:"isPublished"`
	}
	if err := c.ShouldBindJSON(&req); err != nil {
		utils.Error(c, 400, "Invalid request")
		return
	}

	if err := repositories.UpdatePostStatus(postID, req.IsPublished); err != nil {
		utils.ServerError(c, err)
		return
	}

	historySaved := trySavePostHistory(c, postID)

	utils.SuccessWithMsg(c, "Post status updated successfully", gin.H{"historySaved": historySaved})
}

// 删除文章
func AdminDeletePost(c *gin.Context) {
	postIDStr := c.Param("id")
	var postID uint
	if _, err := fmt.Sscanf(postIDStr, "%d", &postID); err != nil {
		utils.Error(c, 400, "Invalid post ID")
		return
	}

	// 删除文章
	if err := repositories.DeletePost(postID); err != nil {
		utils.ServerError(c, err)
		return
	}

	utils.SuccessWithMsg(c, "Post deleted successfully", nil)
}

// 获取文章历史记录
func AdminGetPostHistory(c *gin.Context) {
	postIDStr := c.Param("id")
	var postID uint
	if _, err := fmt.Sscanf(postIDStr, "%d", &postID); err != nil {
		utils.Error(c, 400, "Invalid post ID")
		return
	}

	history, err := repositories.GetPostHistory(postID)
	if err != nil {
		utils.ServerError(c, err)
		return
	}

	res := repositories.BuildPostHistoryResponses(history)
	if res == nil {
		utils.Success(c, []interface{}{})
	} else {
		utils.Success(c, res)
	}
}

// 获取指定版本的文章历史记录
func AdminGetPostHistoryByVersion(c *gin.Context) {
	postIDStr := c.Param("id")
	var postID uint
	if _, err := fmt.Sscanf(postIDStr, "%d", &postID); err != nil {
		utils.Error(c, 400, "Invalid post ID")
		return
	}

	version := c.Param("version")

	// 转换版本号为uint
	var versionUint uint
	_, err := fmt.Sscanf(version, "%d", &versionUint)
	if err != nil {
		utils.Error(c, 400, "Invalid version")
		return
	}

	history, err := repositories.GetPostHistoryByVersion(postID, versionUint)
	if err != nil {
		utils.ServerError(c, err)
		return
	}

	if history == nil {
		utils.Error(c, 404, "History not found")
		return
	}

	utils.Success(c, repositories.BuildPostHistoryResponse(history, true))
}

// AdminGetPostHistoryDiff 对比两个历史版本
func AdminGetPostHistoryDiff(c *gin.Context) {
	postIDStr := c.Param("id")
	var postID uint
	if _, err := fmt.Sscanf(postIDStr, "%d", &postID); err != nil {
		utils.Error(c, 400, "Invalid post ID")
		return
	}

	fromVersion, err := strconv.ParseUint(c.Query("from"), 10, 32)
	if err != nil {
		utils.Error(c, 400, "Invalid from version")
		return
	}
	toVersion, err := strconv.ParseUint(c.Query("to"), 10, 32)
	if err != nil {
		utils.Error(c, 400, "Invalid to version")
		return
	}

	diff, err := repositories.GetPostHistoryDiff(postID, uint(fromVersion), uint(toVersion))
	if err != nil {
		utils.Error(c, 404, err.Error())
		return
	}

	utils.Success(c, diff)
}

// AdminRestorePostHistory 恢复指定历史版本
func AdminRestorePostHistory(c *gin.Context) {
	postIDStr := c.Param("id")
	var postID uint
	if _, err := fmt.Sscanf(postIDStr, "%d", &postID); err != nil {
		utils.Error(c, 400, "Invalid post ID")
		return
	}

	versionStr := c.Param("version")
	versionUint, err := strconv.ParseUint(versionStr, 10, 32)
	if err != nil {
		utils.Error(c, 400, "Invalid version")
		return
	}

	userID, _ := c.Get("userID")
	post, err := repositories.RestorePostFromHistory(postID, uint(versionUint), userID.(uint))
	if err != nil {
		utils.ServerError(c, err)
		return
	}

	utils.Success(c, repositories.BuildPostResponse(post, true))
}

// GetPostsByTagID 根据标签ID获取文章
func GetPostsByTagID(c *gin.Context) {
	tagIDStr := c.Param("id")
	var tagID uint
	if _, err := fmt.Sscanf(tagIDStr, "%d", &tagID); err != nil {
		utils.Error(c, 400, "Invalid tag ID")
		return
	}

	// 获取分页参数
	page, _ := strconv.Atoi(c.DefaultQuery("page", "1"))
	if page < 1 {
		page = 1
	}

	// 从数据库读取 posts_per_page 配置作为默认值
	pageSize := 10 // 默认值
	setting, err := repositories.GetSettingByKey("posts_per_page")
	if err == nil && setting != nil {
		if ps, err := strconv.Atoi(setting.Value); err == nil && ps > 0 {
			pageSize = ps
		}
	}

	// 如果请求中指定了 pageSize，则使用请求的值
	if pageSizeStr := c.Query("pageSize"); pageSizeStr != "" {
		if ps, err := strconv.Atoi(pageSizeStr); err == nil && ps > 0 {
			pageSize = ps
		}
	}

	posts, total, err := repositories.GetPosts("", 0, tagID, 0, page, pageSize)
	if err != nil {
		utils.ServerError(c, err)
		return
	}

	// 构建响应（返回分页格式）
	responses := repositories.BuildPostsResponse(posts)
	if responses == nil {
		responses = []models.PostResponse{}
	}

	// 返回分页格式的响应
	res := gin.H{
		"list":  responses,
		"total": total,
		"page":  page,
		"size":  pageSize,
	}

	utils.Success(c, res)
}

// GetRecommendedPosts 获取推荐文章（基于IP的协同过滤）
func GetRecommendedPosts(c *gin.Context) {
	idStr := c.Param("id")
	var id uint
	if _, err := fmt.Sscanf(idStr, "%d", &id); err != nil {
		utils.Error(c, 400, "Invalid post ID")
		return
	}

	// 获取客户端IP
	userIP := c.ClientIP()

	// 获取推荐文章（默认3篇）
	limit := 3
	if limitStr := c.Query("limit"); limitStr != "" {
		if l, err := strconv.Atoi(limitStr); err == nil && l > 0 && l <= 10 {
			limit = l
		}
	}

	posts, err := repositories.GetRecommendedPostsByIP(id, userIP, limit)
	if err != nil {
		utils.ServerError(c, err)
		return
	}

	// 构建响应
	responses := repositories.BuildPostsResponse(posts)
	if responses == nil {
		utils.Success(c, []interface{}{})
	} else {
		utils.Success(c, responses)
	}
}
