package handler

import (
	"fmt"
	"net/http"
	"os"
	"path/filepath"
	"strings"
	"time"

	"github.com/gin-gonic/gin"

	"sproutworkcollect-backend/internal/model"
	"sproutworkcollect-backend/internal/service"
)

const maxUploadBytes = 5000 << 20 // 5 000 MB

// AdminHandler handles admin-only API endpoints (protected by AdminAuth middleware).
type AdminHandler struct {
	workSvc *service.WorkService
}

// NewAdminHandler wires up an AdminHandler.
func NewAdminHandler(workSvc *service.WorkService) *AdminHandler {
	return &AdminHandler{workSvc: workSvc}
}

// GetWorks handles GET /api/admin/works
func (h *AdminHandler) GetWorks(c *gin.Context) {
	works, err := h.workSvc.LoadAllWorks()
	if err != nil {
		c.JSON(http.StatusInternalServerError, gin.H{"success": false, "message": err.Error()})
		return
	}
	responses := make([]*model.WorkResponse, len(works))
	for i, w := range works {
		responses[i] = h.workSvc.BuildResponse(w)
	}
	c.JSON(http.StatusOK, gin.H{"success": true, "data": responses, "total": len(responses)})
}

// CreateWork handles POST /api/admin/works
func (h *AdminHandler) CreateWork(c *gin.Context) {
	var data model.WorkConfig
	if err := c.ShouldBindJSON(&data); err != nil {
		c.JSON(http.StatusBadRequest, gin.H{"success": false, "message": "请求数据格式错误"})
		return
	}
	if data.WorkID == "" {
		c.JSON(http.StatusBadRequest, gin.H{"success": false, "message": "作品ID不能为空"})
		return
	}

	ts := time.Now().Format("2006-01-02T15:04:05.000000")
	data.UploadTime = ts
	data.UpdateTime = ts
	data.UpdateCount = 0
	data.Downloads = 0
	data.Views = 0
	data.Likes = 0
	data.Normalize()

	if err := h.workSvc.CreateWork(&data); err != nil {
		status := http.StatusInternalServerError
		if strings.Contains(err.Error(), "已存在") {
			status = http.StatusConflict
		}
		c.JSON(status, gin.H{"success": false, "message": err.Error()})
		return
	}
	c.JSON(http.StatusOK, gin.H{"success": true, "message": "创建成功", "work_id": data.WorkID})
}

// UpdateWork handles PUT /api/admin/works/:work_id
func (h *AdminHandler) UpdateWork(c *gin.Context) {
	workID := c.Param("work_id")
	var data model.WorkConfig
	if err := c.ShouldBindJSON(&data); err != nil {
		c.JSON(http.StatusBadRequest, gin.H{"success": false, "message": "请求数据格式错误"})
		return
	}
	if err := h.workSvc.UpdateWork(workID, &data); err != nil {
		status := http.StatusInternalServerError
		if strings.Contains(err.Error(), "不存在") {
			status = http.StatusNotFound
		}
		c.JSON(status, gin.H{"success": false, "message": err.Error()})
		return
	}
	c.JSON(http.StatusOK, gin.H{"success": true, "message": "更新成功"})
}

// DeleteWork handles DELETE /api/admin/works/:work_id
func (h *AdminHandler) DeleteWork(c *gin.Context) {
	if err := h.workSvc.DeleteWork(c.Param("work_id")); err != nil {
		c.JSON(http.StatusNotFound, gin.H{"success": false, "message": err.Error()})
		return
	}
	c.JSON(http.StatusOK, gin.H{"success": true, "message": "删除成功"})
}

// UploadFile handles POST /api/admin/upload/:work_id/:file_type
// file_type: "image" | "video" | "platform"
// For "platform", the form field "platform" must specify the target platform name.
func (h *AdminHandler) UploadFile(c *gin.Context) {
	workID := c.Param("work_id")
	fileType := c.Param("file_type")

	if !h.workSvc.WorkExists(workID) {
		c.JSON(http.StatusNotFound, gin.H{"success": false, "message": "作品不存在"})
		return
	}

	fh, err := c.FormFile("file")
	if err != nil {
		c.JSON(http.StatusBadRequest, gin.H{"success": false, "message": "没有文件"})
		return
	}
	if fh.Size > maxUploadBytes {
		c.JSON(http.StatusRequestEntityTooLarge, gin.H{
			"success": false,
			"message": fmt.Sprintf("文件太大，最大支持 %dMB，当前 %dMB",
				maxUploadBytes>>20, fh.Size>>20),
		})
		return
	}

	originalName := fh.Filename
	safeName := service.SafeFilename(originalName)
	ext := strings.ToLower(filepath.Ext(safeName))

	allowed := map[string]bool{
		".png": true, ".jpg": true, ".jpeg": true, ".gif": true,
		".mp4": true, ".avi": true, ".mov": true,
		".zip": true, ".rar": true, ".apk": true, ".exe": true, ".dmg": true,
	}
	if !allowed[ext] {
		c.JSON(http.StatusBadRequest, gin.H{"success": false, "message": "不支持的文件格式"})
		return
	}

	// Determine destination directory and resolve a unique filename.
	// ModifyWork (called later) re-checks uniqueness under a write lock to avoid races.
	work, err := h.workSvc.LoadWork(workID)
	if err != nil {
		c.JSON(http.StatusNotFound, gin.H{"success": false, "message": "作品配置不存在"})
		return
	}

	var saveDir string
	var platform string

	switch fileType {
	case "image":
		saveDir = filepath.Join(h.workSvc.WorksDir(), workID, "image")
	case "video":
		saveDir = filepath.Join(h.workSvc.WorksDir(), workID, "video")
	case "platform":
		platform = c.PostForm("platform")
		if platform == "" {
			c.JSON(http.StatusBadRequest, gin.H{"success": false, "message": "平台参数缺失"})
			return
		}
		saveDir = filepath.Join(h.workSvc.WorksDir(), workID, "platform", platform)
	default:
		c.JSON(http.StatusBadRequest, gin.H{"success": false, "message": "不支持的文件类型"})
		return
	}

	if err := os.MkdirAll(saveDir, 0755); err != nil {
		c.JSON(http.StatusInternalServerError, gin.H{"success": false, "message": "创建目录失败"})
		return
	}

	// Pre-compute unique filename based on current state.
	// The authoritative assignment happens inside ModifyWork below.
	var previewName string
	switch fileType {
	case "image":
		previewName = service.UniqueFilename(safeName, work.Screenshots)
	case "video":
		previewName = service.UniqueFilename(safeName, work.VideoFiles)
	case "platform":
		previewName = service.UniqueFilename(safeName, work.FileNames[platform])
	}

	destPath := filepath.Join(saveDir, previewName)
	if err := c.SaveUploadedFile(fh, destPath); err != nil {
		c.JSON(http.StatusInternalServerError, gin.H{
			"success": false,
			"message": fmt.Sprintf("保存文件失败: %v", err),
		})
		return
	}

	// Atomically update the work config under a write lock.
	var finalName string
	modErr := h.workSvc.ModifyWork(workID, func(w *model.WorkConfig) {
		// Re-derive the unique name inside the lock to handle concurrent uploads.
		switch fileType {
		case "image":
			finalName = service.UniqueFilename(safeName, w.Screenshots)
		case "video":
			finalName = service.UniqueFilename(safeName, w.VideoFiles)
		case "platform":
			finalName = service.UniqueFilename(safeName, w.FileNames[platform])
		}

		// Rename the file on disk if the finalName differs from the pre-computed one.
		if finalName != previewName {
			newDest := filepath.Join(saveDir, finalName)
			_ = os.Rename(destPath, newDest)
		}

		if w.OriginalNames == nil {
			w.OriginalNames = make(map[string]string)
		}
		w.OriginalNames[finalName] = originalName

		switch fileType {
		case "image":
			if !service.ContainsString(w.Screenshots, finalName) {
				w.Screenshots = append(w.Screenshots, finalName)
			}
			if w.Cover == "" {
				w.Cover = finalName
			}
		case "video":
			if !service.ContainsString(w.VideoFiles, finalName) {
				w.VideoFiles = append(w.VideoFiles, finalName)
			}
		case "platform":
			if w.FileNames == nil {
				w.FileNames = make(map[string][]string)
			}
			if !service.ContainsString(w.FileNames[platform], finalName) {
				w.FileNames[platform] = append(w.FileNames[platform], finalName)
			}
		}
	})

	if modErr != nil {
		c.JSON(http.StatusInternalServerError, gin.H{"success": false, "message": "更新配置失败"})
		return
	}

	c.JSON(http.StatusOK, gin.H{
		"success":   true,
		"message":   "上传成功",
		"filename":  finalName,
		"file_size": fh.Size,
	})
}

// DeleteFile handles DELETE /api/admin/delete-file/:work_id/:file_type/:filename
func (h *AdminHandler) DeleteFile(c *gin.Context) {
	workID := c.Param("work_id")
	fileType := c.Param("file_type")
	filename := c.Param("filename")

	if _, err := h.workSvc.LoadWork(workID); err != nil {
		c.JSON(http.StatusNotFound, gin.H{"success": false, "message": "作品不存在"})
		return
	}

	platform := c.Query("platform")

	var filePath string
	switch fileType {
	case "image":
		filePath = filepath.Join(h.workSvc.WorksDir(), workID, "image", filename)
	case "video":
		filePath = filepath.Join(h.workSvc.WorksDir(), workID, "video", filename)
	case "platform":
		if platform == "" {
			c.JSON(http.StatusBadRequest, gin.H{"success": false, "message": "平台参数缺失"})
			return
		}
		filePath = filepath.Join(h.workSvc.WorksDir(), workID, "platform", platform, filename)
	default:
		c.JSON(http.StatusBadRequest, gin.H{"success": false, "message": "不支持的文件类型"})
		return
	}

	_ = os.Remove(filePath)

	modErr := h.workSvc.ModifyWork(workID, func(w *model.WorkConfig) {
		if w.OriginalNames != nil {
			delete(w.OriginalNames, filename)
		}
		switch fileType {
		case "image":
			w.Screenshots = service.RemoveString(w.Screenshots, filename)
			if w.Cover == filename {
				if len(w.Screenshots) > 0 {
					w.Cover = w.Screenshots[0]
				} else {
					w.Cover = ""
				}
			}
		case "video":
			w.VideoFiles = service.RemoveString(w.VideoFiles, filename)
		case "platform":
			if w.FileNames != nil {
				w.FileNames[platform] = service.RemoveString(w.FileNames[platform], filename)
			}
		}
	})

	if modErr != nil {
		c.JSON(http.StatusInternalServerError, gin.H{"success": false, "message": "更新配置失败"})
		return
	}

	c.JSON(http.StatusOK, gin.H{"success": true, "message": "删除成功"})
}
