package router

import (
	"encoding/base64"
	"fmt"
	"net/http"
	"os"
	"path/filepath"
	"strings"
	"time"
	"ziqian/config"
	u "ziqian/utils"

	"github.com/gin-gonic/gin"
)

// getMimeType 根据文件扩展名获取 MIME 类型
func getMimeType(filename string) string {
	ext := strings.ToLower(filepath.Ext(filename))
	mimeMap := map[string]string{
		".jpg":  "image/jpeg",
		".jpeg": "image/jpeg",
		".png":  "image/png",
		".gif":  "image/gif",
		".webp": "image/webp",
		".bmp":  "image/bmp",
		".svg":  "image/svg+xml",
		".pdf":  "application/pdf",
		".txt":  "text/plain",
		".html": "text/html",
		".json": "application/json",
		".zip":  "application/zip",
		".doc":  "application/msword",
		".docx": "application/vnd.openxmlformats-officedocument.wordprocessingml.document",
		".xls":  "application/vnd.ms-excel",
		".xlsx": "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
	}
	if mime, ok := mimeMap[ext]; ok {
		return mime
	}
	return "application/octet-stream"
}

// fileExists 检查文件是否存在
func fileExists(path string) bool {
	_, err := os.Stat(path)
	return err == nil
}

// resolveFilePath 将请求路径转换为存储服务器上的绝对路径
func resolveFilePath(storageRoot, requestPath string) string {
	// 去除开头的斜杠，避免 filepath.Join 处理异常
	cleanPath := strings.TrimPrefix(requestPath, "/")
	return filepath.Join(storageRoot, cleanPath)
}

func SetupRouter() *gin.Engine {
	r := gin.Default()
	cfg := config.GetConfig()

	r.LoadHTMLFiles("templates/index.html")

	// 只在开发环境中配置静态文件服务
	if gin.Mode() == gin.DebugMode {
		r.Static("/static", "./static")
		fmt.Println("Development mode: Static file services enabled")
	} else {
		fmt.Println("Production mode: Static file services disabled")
	}

	// 添加CORS中间件
	r.Use(func(c *gin.Context) {
		c.Writer.Header().Set("Access-Control-Allow-Origin", "*")
		c.Writer.Header().Set("Access-Control-Allow-Credentials", "true")
		c.Writer.Header().Set("Access-Control-Allow-Headers", "Content-Type, Content-Length, Accept-Encoding, X-CSRF-Token, Authorization, accept, origin, Cache-Control, X-Requested-With")
		c.Writer.Header().Set("Access-Control-Allow-Methods", "POST, OPTIONS, GET, PUT, DELETE")

		if c.Request.Method == "OPTIONS" {
			c.AbortWithStatus(204)
			return
		}

		c.Next()
	})

	r.GET("/", func(c *gin.Context) {
		u.Zi.Html(c, "index.html", gin.H{
			"Title":       "Ziqian API",
			"Subtitle":    "基于 Gin 框架的 API 服务",
			"CurrentTime": time.Now().Format("2006-01-02 15:04:05"),
			"Version":     "1.0.0",
			"Year":        time.Now().Year(),
		})
	})

	apiGroup := r.Group("/api")
	{
		apiGroup.Any("/ho", func(c *gin.Context) {
			u.Zi.Echo(c, gin.H{
				"status": "ok",
			})
		})

		apiGroup.Any("/map", func(c *gin.Context) {
			cParam := c.Query("c")
			typeList := strings.Split(cParam, ",")
			if cParam == "" {
				typeList = nil
			}

			processed := make(map[string]bool)
			apiRoutes := make(map[string]string)

			protocol := "http"
			xForwardedProto := c.GetHeader("X-Forwarded-Proto")
			xScheme := c.GetHeader("X-Scheme")
			if c.Request.TLS != nil || xForwardedProto == "https" || xScheme == "https" {
				protocol = "https"
			}
			host := c.Request.Host

			for _, route := range r.Routes() {
				path := route.Path
				if !strings.HasPrefix(path, "/api/") || path == "/api/map" || processed[path] {
					continue
				}
				processed[path] = true

				keyName := ""
				for i, part := range strings.Split(strings.TrimPrefix(path, "/api/"), "/") {
					if part == "" {
						continue
					}
					upperPart := strings.ToUpper(string(part[0])) + part[1:]
					keyName += func() string {
						if i > 0 {
							return "_" + upperPart
						}
						return upperPart
					}()
				}

				if typeList != nil {
					typePrefix := strings.ToLower(strings.Split(keyName, "_")[0])
					matched := false
					for _, t := range typeList {
						if strings.ToLower(t) == typePrefix {
							matched = true
							break
						}
					}
					if !matched {
						continue
					}
				}

				apiRoutes[keyName] = fmt.Sprintf("%s://%s%s", protocol, host, path)
			}

			apiRoutes["Map"] = fmt.Sprintf("%s://%s%s", protocol, host, "/api/map")
			apiRoutes["Ho"] = fmt.Sprintf("%s://%s%s", protocol, host, "/api/ho")

			u.Zi.Echo(c, apiRoutes)
		})

		// 上传文件 - base64
		apiGroup.POST("/upload/base64", func(c *gin.Context) {
			var req struct {
				File     string `json:"file"`
				Path     string `json:"path"`
				Filename string `json:"filename"`
			}
			if err := c.ShouldBindJSON(&req); err != nil {
				u.Zi.Error(c, "请求参数错误")
				return
			}

			if req.File == "" || req.Filename == "" {
				u.Zi.Error(c, "文件内容或文件名不能为空")
				return
			}

			// 解码 base64
			decoded, err := base64.StdEncoding.DecodeString(req.File)
			if err != nil {
				u.Zi.Error(c, "base64 解码失败")
				return
			}

			// 构建完整路径
			dirPath := resolveFilePath(cfg.Storage.Root, req.Path)
			fullPath := filepath.Join(dirPath, req.Filename)

			// 创建目录
			if err := os.MkdirAll(dirPath, 0755); err != nil {
				u.Zi.Error(c, "创建目录失败: "+err.Error())
				return
			}

			// 写入文件
			if err := os.WriteFile(fullPath, decoded, 0644); err != nil {
				u.Zi.Error(c, "写入文件失败: "+err.Error())
				return
			}

			// 构建返回 URL
			relPath := strings.TrimPrefix(req.Path, "/")
			url := fmt.Sprintf("/files/%s/%s", relPath, req.Filename)

			u.Zi.Echo(c, gin.H{
				"url": url,
			})
		})

		// 上传文件 - multipart
		apiGroup.POST("/upload/file", func(c *gin.Context) {
			file, err := c.FormFile("file")
			if err != nil {
				u.Zi.Error(c, "获取上传文件失败")
				return
			}

			path := c.PostForm("path")
			if path == "" {
				u.Zi.Error(c, "路径不能为空")
				return
			}

			// 构建完整路径
			dirPath := resolveFilePath(cfg.Storage.Root, path)
			fullPath := filepath.Join(dirPath, file.Filename)

			// 创建目录
			if err := os.MkdirAll(dirPath, 0755); err != nil {
				u.Zi.Error(c, "创建目录失败: "+err.Error())
				return
			}

			// 保存文件
			if err := c.SaveUploadedFile(file, fullPath); err != nil {
				u.Zi.Error(c, "保存文件失败: "+err.Error())
				return
			}

			// 构建返回 URL
			relPath := strings.TrimPrefix(path, "/")
			url := fmt.Sprintf("/files/%s/%s", relPath, file.Filename)

			u.Zi.Echo(c, gin.H{
				"url": url,
			})
		})
	}

	// 文件服务路由 - 处理 GET/HEAD/DELETE
	r.Any("/files/*filepath", func(c *gin.Context) {
		filePath := c.Param("filepath")

		// 安全保护：不允许空路径或根路径操作
		if filePath == "" || filePath == "/" {
			c.Status(http.StatusBadRequest)
			return
		}

		fullPath := resolveFilePath(cfg.Storage.Root, filePath)

		// 安全保护：确保解析后的路径仍在存储根目录下
		cleanRoot := filepath.Clean(cfg.Storage.Root)
		cleanFull := filepath.Clean(fullPath)
		if !strings.HasPrefix(cleanFull, cleanRoot) {
			c.Status(http.StatusForbidden)
			return
		}

		switch c.Request.Method {
		case http.MethodGet:
			if !fileExists(fullPath) {
				c.Status(http.StatusNotFound)
				return
			}
			c.File(fullPath)

		case http.MethodHead:
			info, err := os.Stat(fullPath)
			if err != nil {
				c.Status(http.StatusNotFound)
				return
			}
			c.Header("Content-Type", getMimeType(filePath))
			c.Header("Content-Length", fmt.Sprintf("%d", info.Size()))
			c.Header("Last-Modified", info.ModTime().Format(http.TimeFormat))
			c.Status(http.StatusOK)

		case http.MethodDelete:
			if err := os.RemoveAll(fullPath); err != nil {
				c.Status(http.StatusInternalServerError)
				return
			}
			c.Status(http.StatusOK)

		default:
			c.Status(http.StatusMethodNotAllowed)
		}
	})

	// 确保文件上传中间件中不包含日志记录（已移除）
	// 如果需要将上传的文件写入到磁盘中的指定路径，请使用上述接口

	return r
}