package handler

import (
	"crypto/rand"
	"crypto/sha256"
	"encoding/hex"
	"fmt"
	"image"
	"image/color"
	"image/jpeg"
	"image/png"
	"io"
	"git.yeij.top/AskaEth/Cyrene/pkg/logger"
	"net/http"
	"os"
	"path/filepath"
	"regexp"
	"strconv"
	"strings"
	"time"

	"github.com/gin-gonic/gin"

	"git.yeij.top/AskaEth/Cyrene/gateway/internal/middleware"
	"git.yeij.top/AskaEth/Cyrene/gateway/internal/store"
)

// FileHandler 文件管理处理器
type FileHandler struct {
	store     *store.FileStore
	uploadDir string
}

// NewFileHandler 创建文件处理器
func NewFileHandler(s *store.FileStore) *FileHandler {
	return &FileHandler{
		store:     s,
		uploadDir: "./uploads",
	}
}

// ========== 允许的文件类型 ==========

var allowedMimeTypes = map[string]bool{
	// 图片
	"image/jpeg": true,
	"image/png":  true,
	"image/gif":  true,
	"image/webp": true,
	"image/svg+xml": true,
	// 文档
	"application/pdf":    true,
	"text/plain":         true,
	"text/markdown":      true,
	"application/msword": true,
	"application/vnd.openxmlformats-officedocument.wordprocessingml.document": true,
	// 音频
	"audio/mpeg": true,
	"audio/wav":  true,
	"audio/ogg":  true,
	"audio/webm": true,
	// 视频
	"video/mp4":  true,
	"video/webm": true,
}

var allowedExtensions = map[string]string{
	".jpg":  "image/jpeg",
	".jpeg": "image/jpeg",
	".png":  "image/png",
	".gif":  "image/gif",
	".webp": "image/webp",
	".svg":  "image/svg+xml",
	".pdf":  "application/pdf",
	".txt":  "text/plain",
	".md":   "text/markdown",
	".doc":  "application/msword",
	".docx": "application/vnd.openxmlformats-officedocument.wordprocessingml.document",
	".mp3":  "audio/mpeg",
	".wav":  "audio/wav",
	".ogg":  "audio/ogg",
	".mp4":  "video/mp4",
}

const maxFileSize = 20 * 1024 * 1024 // 20MB

// ========== POST /api/v1/files/upload ==========

// Upload 处理文件上传
func (h *FileHandler) Upload(c *gin.Context) {
	userID := middleware.GetUserID(c)

	// Nil store guard — 数据库不可用时拒绝上传
	if h.store == nil {
		c.JSON(http.StatusServiceUnavailable, gin.H{"error": "文件存储不可用，数据库未连接", "errorType": "service_unavailable"})
		return
	}

	// 获取上传的文件
	file, header, err := c.Request.FormFile("file")
	if err != nil {
		c.JSON(http.StatusBadRequest, gin.H{"error": "未找到上传文件", "errorType": "missing_file"})
		return
	}
	defer file.Close()

	// 检查文件大小
	if header.Size > maxFileSize {
		c.JSON(http.StatusBadRequest, gin.H{
			"error":     "文件大小超过限制 (最大 20MB)",
			"errorType": "file_too_large",
		})
		return
	}

	// 验证文件类型
	mimeType := header.Header.Get("Content-Type")
	ext := strings.ToLower(filepath.Ext(header.Filename))
	if mimeType == "" || mimeType == "application/octet-stream" {
		// 尝试从扩展名推断
		if inferred, ok := allowedExtensions[ext]; ok {
			mimeType = inferred
		}
	}
	if !allowedMimeTypes[mimeType] {
		// 再尝试通过扩展名检查
		if _, ok := allowedExtensions[ext]; !ok {
			c.JSON(http.StatusBadRequest, gin.H{
				"error":     "不支持的文件类型: " + mimeType,
				"errorType": "unsupported_type",
			})
			return
		}
		mimeType = allowedExtensions[ext]
	}

	// 安全化文件名：移除路径分隔符和特殊字符
	safeFilename := sanitizeFilename(header.Filename)

	// 生成文件ID (crypto/rand UUID v4)
	fileID := generateUUID()

	// 创建按日期组织的目录
	dateDir := time.Now().Format("2006-01-02")
	storedDir := filepath.Join(h.uploadDir, dateDir)
	if err := os.MkdirAll(storedDir, 0755); err != nil {
		logger.Printf("[FileHandler] 创建上传目录失败: %v", err)
		c.JSON(http.StatusInternalServerError, gin.H{"error": "创建上传目录失败", "errorType": "server_error"})
		return
	}

	// 存储路径：以UUID+原始扩展名保存
	storedFilename := fileID + ext
	storedPath := filepath.Join(storedDir, storedFilename)

	// 计算 SHA256 hash
	hasher := sha256.New()
	teeReader := io.TeeReader(file, hasher)

	// 保存到磁盘
	dst, err := os.Create(storedPath)
	if err != nil {
		logger.Printf("[FileHandler] 创建文件失败: %v", err)
		c.JSON(http.StatusInternalServerError, gin.H{"error": "保存文件失败", "errorType": "server_error"})
		return
	}
	defer dst.Close()

	written, err := io.Copy(dst, teeReader)
	if err != nil {
		os.Remove(storedPath)
		logger.Printf("[FileHandler] 写入文件失败: %v", err)
		c.JSON(http.StatusInternalServerError, gin.H{"error": "写入文件失败", "errorType": "server_error"})
		return
	}

	hash := hex.EncodeToString(hasher.Sum(nil))

	// 去重检查：如果存在相同hash的文件，复用已有记录
	if existing, err := h.store.GetFileByHash(hash); err == nil && existing != nil {
		// 删除刚保存的重复文件
		os.Remove(storedPath)
		logger.Printf("[FileHandler] 文件去重: 复用已有文件 %s (hash=%s)", existing.ID, hash[:16])

		c.JSON(http.StatusOK, gin.H{
			"id":        existing.ID,
			"filename":  existing.Filename,
			"mime_type": existing.MimeType,
			"size":      existing.Size,
			"url":       fmt.Sprintf("/api/v1/files/%s/download", existing.ID),
			"dedup":     true,
		})
		return
	}

	// 创建数据库记录
	fileRecord := &store.File{
		ID:         fileID,
		UserID:     userID,
		Filename:   safeFilename,
		StoredPath: storedPath,
		MimeType:   mimeType,
		Size:       written,
		Hash:       hash,
		IsPublic:   false,
		CreatedAt:  time.Now(),
	}

	if err := h.store.CreateFile(fileRecord); err != nil {
		os.Remove(storedPath)
		logger.Printf("[FileHandler] 创建文件记录失败: %v", err)
		c.JSON(http.StatusInternalServerError, gin.H{"error": "创建文件记录失败", "errorType": "db_error"})
		return
	}

	logger.Printf("[FileHandler] 文件上传成功: %s (%s, %d bytes, hash=%s)", fileID, safeFilename, written, hash[:16])

	c.JSON(http.StatusCreated, gin.H{
		"id":        fileID,
		"filename":  safeFilename,
		"mime_type": mimeType,
		"size":      written,
		"url":       fmt.Sprintf("/api/v1/files/%s/download", fileID),
	})
}

// ========== GET /api/v1/files ==========

// List 列出用户的所有文件 (支持分页)
func (h *FileHandler) List(c *gin.Context) {
	userID := middleware.GetUserID(c)

	if h.store == nil {
		c.JSON(http.StatusServiceUnavailable, gin.H{"error": "文件存储不可用", "errorType": "service_unavailable"})
		return
	}

	page, _ := strconv.Atoi(c.DefaultQuery("page", "1"))
	limit, _ := strconv.Atoi(c.DefaultQuery("limit", "20"))

	files, total, err := h.store.GetUserFiles(userID, page, limit)
	if err != nil {
		logger.Printf("[FileHandler] 查询文件列表失败: %v", err)
		c.JSON(http.StatusInternalServerError, gin.H{"error": "查询文件列表失败", "errorType": "db_error"})
		return
	}

	// 转换为响应格式
	result := make([]gin.H, 0, len(files))
	for _, f := range files {
		item := gin.H{
			"id":         f.ID,
			"user_id":    f.UserID,
			"filename":   f.Filename,
			"mime_type":  f.MimeType,
			"size":       f.Size,
			"hash":       f.Hash,
			"is_public":  f.IsPublic,
			"created_at": f.CreatedAt.UnixMilli(),
			"url":        fmt.Sprintf("/api/v1/files/%s/download", f.ID),
		}
		// 图片类型添加缩略图URL
		if isImageType(f.MimeType) {
			item["thumbnail_url"] = fmt.Sprintf("/api/v1/files/%s/thumbnail", f.ID)
		}
		result = append(result, item)
	}

	c.JSON(http.StatusOK, gin.H{
		"files": result,
		"total": total,
		"page":  page,
		"limit": limit,
	})
}

// ========== GET /api/v1/files/:id ==========

// Get 获取文件元数据
func (h *FileHandler) Get(c *gin.Context) {
	userID := middleware.GetUserID(c)
	fileID := c.Param("id")

	if h.store == nil {
		c.JSON(http.StatusServiceUnavailable, gin.H{"error": "文件存储不可用", "errorType": "service_unavailable"})
		return
	}

	f, err := h.store.GetFile(fileID)
	if err != nil {
		logger.Printf("[FileHandler] 查询文件失败: %v", err)
		c.JSON(http.StatusInternalServerError, gin.H{"error": "查询文件失败", "errorType": "db_error"})
		return
	}
	if f == nil {
		c.JSON(http.StatusNotFound, gin.H{
			"error":     "文件不存在",
			"errorType": "file_not_found",
		})
		return
	}

	// 检查权限
	if f.UserID != userID && !f.IsPublic {
		c.JSON(http.StatusForbidden, gin.H{"error": "无权访问此文件", "errorType": "access_denied"})
		return
	}

	item := gin.H{
		"id":         f.ID,
		"user_id":    f.UserID,
		"filename":   f.Filename,
		"mime_type":  f.MimeType,
		"size":       f.Size,
		"hash":       f.Hash,
		"is_public":  f.IsPublic,
		"created_at": f.CreatedAt.UnixMilli(),
		"url":        fmt.Sprintf("/api/v1/files/%s/download", f.ID),
	}
	if isImageType(f.MimeType) {
		item["thumbnail_url"] = fmt.Sprintf("/api/v1/files/%s/thumbnail", f.ID)
	}

	c.JSON(http.StatusOK, item)
}

// ========== GET /api/v1/files/:id/download ==========

// Download 下载文件
func (h *FileHandler) Download(c *gin.Context) {
	userID := middleware.GetUserID(c)
	fileID := c.Param("id")

	if h.store == nil {
		c.JSON(http.StatusServiceUnavailable, gin.H{"error": "文件存储不可用", "errorType": "service_unavailable"})
		return
	}

	f, err := h.store.GetFile(fileID)
	if err != nil {
		logger.Printf("[FileHandler] 查询文件失败: %v", err)
		c.JSON(http.StatusInternalServerError, gin.H{"error": "查询文件失败", "errorType": "db_error"})
		return
	}
	if f == nil {
		c.JSON(http.StatusNotFound, gin.H{
			"error":     "文件不存在",
			"errorType": "file_not_found",
		})
		return
	}

	// 检查权限
	if f.UserID != userID && !f.IsPublic {
		c.JSON(http.StatusForbidden, gin.H{"error": "无权访问此文件", "errorType": "access_denied"})
		return
	}

	// 检查文件在磁盘上是否存在
	if _, err := os.Stat(f.StoredPath); os.IsNotExist(err) {
		c.JSON(http.StatusNotFound, gin.H{
			"error":     "文件实体不存在（可能已被清理）",
			"errorType": "file_missing",
		})
		return
	}

	// 设置 Content-Disposition
	c.Header("Content-Disposition", fmt.Sprintf(`attachment; filename="%s"`, f.Filename))
	c.Header("Content-Type", f.MimeType)
	c.File(f.StoredPath)
}

// ========== DELETE /api/v1/files/:id ==========

// Delete 删除文件
func (h *FileHandler) Delete(c *gin.Context) {
	userID := middleware.GetUserID(c)
	fileID := c.Param("id")

	if h.store == nil {
		c.JSON(http.StatusServiceUnavailable, gin.H{"error": "文件存储不可用", "errorType": "service_unavailable"})
		return
	}

	f, err := h.store.GetFile(fileID)
	if err != nil {
		logger.Printf("[FileHandler] 查询文件失败: %v", err)
		c.JSON(http.StatusInternalServerError, gin.H{"error": "查询文件失败", "errorType": "db_error"})
		return
	}
	if f == nil {
		c.JSON(http.StatusNotFound, gin.H{
			"error":     "文件不存在",
			"errorType": "file_not_found",
		})
		return
	}

	// 检查权限
	if f.UserID != userID {
		c.JSON(http.StatusForbidden, gin.H{"error": "无权删除此文件", "errorType": "access_denied"})
		return
	}

	// 删除磁盘上的文件（忽略错误，可能已被删除）
	if err := os.Remove(f.StoredPath); err != nil && !os.IsNotExist(err) {
		logger.Printf("[FileHandler] 删除磁盘文件失败 (stored_path=%s): %v", f.StoredPath, err)
	}

	// 删除数据库记录
	if err := h.store.DeleteFile(fileID); err != nil {
		logger.Printf("[FileHandler] 删除文件记录失败: %v", err)
		c.JSON(http.StatusInternalServerError, gin.H{"error": "删除文件记录失败", "errorType": "db_error"})
		return
	}

	c.JSON(http.StatusOK, gin.H{"status": "deleted"})
}

// ========== GET /api/v1/files/:id/thumbnail ==========

// Thumbnail 返回文件缩略图
func (h *FileHandler) Thumbnail(c *gin.Context) {
	userID := middleware.GetUserID(c)
	fileID := c.Param("id")

	if h.store == nil {
		c.JSON(http.StatusServiceUnavailable, gin.H{"error": "文件存储不可用", "errorType": "service_unavailable"})
		return
	}

	f, err := h.store.GetFile(fileID)
	if err != nil {
		logger.Printf("[FileHandler] 查询文件失败: %v", err)
		c.JSON(http.StatusInternalServerError, gin.H{"error": "查询文件失败", "errorType": "db_error"})
		return
	}
	if f == nil {
		c.JSON(http.StatusNotFound, gin.H{
			"error":     "文件不存在",
			"errorType": "file_not_found",
		})
		return
	}

	if f.UserID != userID && !f.IsPublic {
		c.JSON(http.StatusForbidden, gin.H{"error": "无权访问此文件", "errorType": "access_denied"})
		return
	}

	// 如果是图片，生成缩略图
	if isImageType(f.MimeType) && f.MimeType != "image/svg+xml" {
		if thumbData, contentType, err := generateThumbnail(f.StoredPath, f.MimeType); err == nil {
			c.Header("Content-Type", contentType)
			c.Header("Cache-Control", "public, max-age=86400")
			c.Data(http.StatusOK, contentType, thumbData)
			return
		} else {
			logger.Printf("[FileHandler] 生成缩略图失败: %v", err)
		}
	}

	// 非图片文件或缩略图生成失败，返回占位图标 SVG
	placeholder := generatePlaceholderSVG(f.MimeType)
	c.Header("Content-Type", "image/svg+xml")
	c.Header("Cache-Control", "public, max-age=86400")
	c.Data(http.StatusOK, "image/svg+xml", []byte(placeholder))
}

// ========== 辅助函数 ==========

// isImageType 判断是否图片类型
func isImageType(mimeType string) bool {
	return strings.HasPrefix(mimeType, "image/")
}

// sanitizeFilename 安全化文件名：移除路径分隔符、特殊字符
var unsafeChars = regexp.MustCompile(`[\\/:*?"<>|]`)

func sanitizeFilename(name string) string {
	// 移除路径分隔符和Windows特殊字符
	name = unsafeChars.ReplaceAllString(name, "_")
	// 限制长度
	if len(name) > 255 {
		ext := filepath.Ext(name)
		base := name[:255-len(ext)]
		name = base + ext
	}
	// 为空时给默认名
	if name == "" || name == "." || name == ".." {
		name = "unnamed_file"
	}
	return name
}

// generateThumbnail 使用 Go 标准库生成缩略图 (最大 300x300)
func generateThumbnail(filePath, mimeType string) ([]byte, string, error) {
	// 打开源文件
	srcFile, err := os.Open(filePath)
	if err != nil {
		return nil, "", fmt.Errorf("打开文件失败: %w", err)
	}
	defer srcFile.Close()

	// 解码图像
	var srcImg image.Image
	switch mimeType {
	case "image/jpeg":
		srcImg, err = jpeg.Decode(srcFile)
	case "image/png":
		srcImg, err = png.Decode(srcFile)
	default:
		// 尝试通用解码
		srcImg, _, err = image.Decode(srcFile)
	}
	if err != nil {
		return nil, "", fmt.Errorf("解码图像失败: %w", err)
	}

	// 计算缩略图尺寸
	bounds := srcImg.Bounds()
	srcW := bounds.Dx()
	srcH := bounds.Dy()

	maxDim := 300
	newW, newH := srcW, srcH
	if srcW > maxDim || srcH > maxDim {
		if srcW > srcH {
			newW = maxDim
			newH = srcH * maxDim / srcW
		} else {
			newH = maxDim
			newW = srcW * maxDim / srcH
		}
	}
	if newW < 1 {
		newW = 1
	}
	if newH < 1 {
		newH = 1
	}

	// 使用标准库双线性缩放
	thumbImg := image.NewRGBA(image.Rect(0, 0, newW, newH))
	scaleBilinear(thumbImg, srcImg)

	// 编码为 JPEG 输出
	var buf strings.Builder
	errWriter := &stringWriter{&buf}
	if err := jpeg.Encode(errWriter, thumbImg, &jpeg.Options{Quality: 80}); err != nil {
		return nil, "", fmt.Errorf("编码缩略图失败: %w", err)
	}

	return []byte(buf.String()), "image/jpeg", nil
}

// stringWriter 实现 io.Writer 到 strings.Builder
type stringWriter struct {
	b *strings.Builder
}

func (w *stringWriter) Write(p []byte) (int, error) {
	return w.b.Write(p)
}

// generatePlaceholderSVG 为非图片文件生成占位图标 SVG
func generatePlaceholderSVG(mimeType string) string {
	var icon, color string

	switch {
	case strings.HasPrefix(mimeType, "audio/"):
		icon = "🎵"
		color = "#8B5CF6" // purple
	case strings.HasPrefix(mimeType, "video/"):
		icon = "🎬"
		color = "#EF4444" // red
	case strings.HasPrefix(mimeType, "application/pdf"):
		icon = "📄"
		color = "#F59E0B" // amber
	case strings.HasPrefix(mimeType, "text/"):
		icon = "📝"
		color = "#3B82F6" // blue
	default:
		icon = "📎"
		color = "#6B7280" // gray
	}

	return fmt.Sprintf(`<svg xmlns="http://www.w3.org/2000/svg" width="300" height="300" viewBox="0 0 300 300">
  <rect width="300" height="300" fill="%s" opacity="0.1"/>
  <text x="150" y="160" text-anchor="middle" font-size="64" fill="%s">%s</text>
  <text x="150" y="220" text-anchor="middle" font-size="16" fill="%s" opacity="0.7">%s</text>
</svg>`, color, color, icon, color, getMimeTypeShort(mimeType))
}

// getMimeTypeShort 获取MIME类型简称
func getMimeTypeShort(mimeType string) string {
	parts := strings.SplitN(mimeType, "/", 2)
	if len(parts) < 2 {
		return mimeType
	}
	return strings.ToUpper(parts[1])
}

// generateUUID 使用 crypto/rand 生成 UUID v4 格式的字符串
func generateUUID() string {
	b := make([]byte, 16)
	if _, err := rand.Read(b); err != nil {
		// 降级方案：基于时间戳 + 随机数的唯一标识
		b = make([]byte, 16)
		ts := time.Now().UnixNano()
		for i := 0; i < 8; i++ {
			b[i] = byte(ts >> (i * 8))
		}
		// 用简单 PRNG 填充剩余字节
		for i := 8; i < 16; i++ {
			b[i] = byte((ts * int64(i+1)) % 256)
		}
	}
	// 设置 UUID v4 版本位 (version = 4)
	b[6] = (b[6] & 0x0f) | 0x40
	// 设置 UUID variant 位 (variant = 10xx)
	b[8] = (b[8] & 0x3f) | 0x80
	return fmt.Sprintf("%08x-%04x-%04x-%04x-%012x",
		b[0:4], b[4:6], b[6:8], b[8:10], b[10:16])
}

// scaleBilinear 使用双线性插值将 src 图像缩放到 dst 的尺寸 (纯标准库实现)
func scaleBilinear(dst *image.RGBA, src image.Image) {
	dstBounds := dst.Bounds()
	srcBounds := src.Bounds()
	dstW := dstBounds.Dx()
	dstH := dstBounds.Dy()
	srcW := srcBounds.Dx()
	srcH := srcBounds.Dy()

	// 计算缩放比例
	scaleX := float64(srcW) / float64(dstW)
	scaleY := float64(srcH) / float64(dstH)

	for dy := 0; dy < dstH; dy++ {
		for dx := 0; dx < dstW; dx++ {
			// 源图像中的浮点坐标
			sx := float64(dx)*scaleX + float64(srcBounds.Min.X)
			sy := float64(dy)*scaleY + float64(srcBounds.Min.Y)

			// 四个邻近像素的整数坐标
			x0 := int(sx)
			y0 := int(sy)
			x1 := x0 + 1
			y1 := y0 + 1

			// 边界限制
			if x0 < srcBounds.Min.X {
				x0 = srcBounds.Min.X
			}
			if x1 >= srcBounds.Max.X {
				x1 = srcBounds.Max.X - 1
			}
			if x0 >= srcBounds.Max.X {
				x0 = srcBounds.Max.X - 1
			}
			if x1 < srcBounds.Min.X {
				x1 = srcBounds.Min.X
			}
			if y0 < srcBounds.Min.Y {
				y0 = srcBounds.Min.Y
			}
			if y1 >= srcBounds.Max.Y {
				y1 = srcBounds.Max.Y - 1
			}
			if y0 >= srcBounds.Max.Y {
				y0 = srcBounds.Max.Y - 1
			}
			if y1 < srcBounds.Min.Y {
				y1 = srcBounds.Min.Y
			}

			// 插值权重
			fracX := sx - float64(x0)
			fracY := sy - float64(y0)

			// 四个角的 RGBA 值
			r00, g00, b00, a00 := src.At(x0, y0).RGBA()
			r10, g10, b10, a10 := src.At(x1, y0).RGBA()
			r01, g01, b01, a01 := src.At(x0, y1).RGBA()
			r11, g11, b11, a11 := src.At(x1, y1).RGBA()

			// 双线性插值 (在 0-65535 范围内进行)
			interp := func(c00, c10, c01, c11 uint32) uint8 {
				top := float64(c00)*(1-fracX) + float64(c10)*fracX
				bot := float64(c01)*(1-fracX) + float64(c11)*fracX
				val := top*(1-fracY) + bot*fracY
				return uint8(val / 256)
			}

			r := interp(r00, r10, r01, r11)
			g := interp(g00, g10, g01, g11)
			b := interp(b00, b10, b01, b11)
			a := interp(a00, a10, a01, a11)

			dst.SetRGBA(dx+int(dstBounds.Min.X), dy+int(dstBounds.Min.Y), color.RGBA{r, g, b, a})
		}
	}
}

