package common

import (
	"fmt"
	"hash/crc32"
	"io/fs"
	"log"
	"net/http"
	"net/url"
	"os"
	"path/filepath"
	"regexp"
	"strconv"

	"metazone.cc/metalab/internal/model"

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

// assetHashes 静态资源文件哈希表（URL 路径 → 短哈希，文件不变哈希不变）
var assetHashes map[string]string

// ComputeAssetHashes 遍历 static 目录，计算每个文件的 CRC32 作为版本号
func ComputeAssetHashes(templateDir string) {
	assetHashes = make(map[string]string)
	// 前端主题：templates/MetaLab-2026/static → URL /static/
	walkStaticDir(filepath.Join(templateDir, "static"), "/static")
	// 共享资源：templates/shared/static → URL /shared/static/
	walkStaticDir(filepath.Join(filepath.Dir(templateDir), "shared", "static"), "/shared/static")
	// 管理后台：templates/admin/static → URL /admin/static/
	walkStaticDir(filepath.Join(filepath.Dir(templateDir), "admin", "static"), "/admin/static")
	log.Printf("[AssetHashes] computed %d file hashes", len(assetHashes))
}

// walkStaticDir 遍历目录，计算每个 .js/.css 的 CRC32，存入 assetHashes
func walkStaticDir(dir, urlPrefix string) {
	filepath.WalkDir(dir, func(path string, d fs.DirEntry, err error) error {
		if err != nil || d.IsDir() {
			return nil
		}
		ext := filepath.Ext(path)
		if ext != ".js" && ext != ".css" {
			return nil
		}
		data, err := os.ReadFile(path)
		if err != nil {
			return nil
		}
		h := crc32.ChecksumIEEE(data)
		shortHash := strconv.FormatUint(uint64(h), 16)
		// 生成 URL 路径：urlPrefix + 相对于 dir 的子路径
		rel, _ := filepath.Rel(dir, path)
		urlPath := urlPrefix + "/" + filepath.ToSlash(rel)
		assetHashes[urlPath] = shortHash
		return nil
	})
}

// AssetV 返回指定静态资源的哈希版本号（模板函数）
func AssetV(path string) string {
	if h, ok := assetHashes[path]; ok {
		return h
	}
	return "0"
}

// mdImageRe 匹配 Markdown 图片语法 ![alt](url)
var mdImageRe = regexp.MustCompile(`!\[.*?\]\((.*?)\)`)

// ExtractFirstImage 从 Markdown 正文提取第一张图片 URL
func ExtractFirstImage(md string) string {
	match := mdImageRe.FindStringSubmatch(md)
	if len(match) > 1 {
		return match[1]
	}
	return ""
}

// BuildPageData 构建页面模板数据，自动注入登录状态、站点信息与 CSRF token
func BuildPageData(c *gin.Context, extra gin.H) gin.H {
	data := gin.H{}
	for k, v := range extra {
		data[k] = v
	}
	if username, exists := c.Get("username"); exists {
		data["IsLoggedIn"] = true
		data["Username"] = username
	}
	// 注入 CSRF token（由 CSRF 中间件设置到上下文中）
	if token, exists := c.Get("csrf_token"); exists {
		data["CSRFToken"] = token
	}
	// 注入站点信息（由 InjectSiteInfo 中间件设置）
	injectSiteInfo(c, data)
	// 注入管理入口权限（moderator 及以上可看到页脚管理面板链接）
	if role, exists := c.Get("role"); exists {
		data["CanAccessAdmin"] = model.HasMinRole(role.(string), model.RoleModerator)
		data["IsOwner"] = model.HasMinRole(role.(string), model.RoleOwner)
	}
	// 注入维护状态
	if isMaintenance, exists := c.Get("is_maintenance"); exists {
		data["IsMaintenance"] = isMaintenance
	}
	return data
}

// BuildAdminPageData 构建管理后台模板数据
// 额外注入 UID、Role、CanManageUsers、CurrentPath 等权限信息
func BuildAdminPageData(c *gin.Context, extra gin.H) gin.H {
	data := BuildPageData(c, extra)
	data["IsAdmin"] = true
	data["CurrentPath"] = c.Request.URL.Path

	if uid, exists := c.Get("uid"); exists {
		data["UID"] = uid
	}
	if role, exists := c.Get("role"); exists {
		roleStr := role.(string)
		data["Role"] = roleStr
		if name, ok := model.RoleDisplayNames[roleStr]; ok {
			data["RoleDisplayName"] = name
		} else {
			data["RoleDisplayName"] = roleStr
		}
		data["CanManageUsers"] = model.HasMinRole(roleStr, model.RoleAdmin)
		data["CanManageAudits"] = model.HasMinRole(roleStr, model.RoleAdmin)
		data["CanManageSettings"] = model.HasMinRole(roleStr, model.RoleOwner)
		data["IsOwner"] = model.HasMinRole(roleStr, model.RoleOwner)
	}
	return data
}

// RedirectToLogin 重定向到登录页，携带当前页面路径作为 redirect 参数
func RedirectToLogin(c *gin.Context) {
	returnURL := url.QueryEscape(c.Request.URL.Path)
	if c.Request.URL.RawQuery != "" {
		returnURL = url.QueryEscape(c.Request.URL.Path + "?" + c.Request.URL.RawQuery)
	}
	c.Redirect(http.StatusFound, fmt.Sprintf("/auth/login?redirect=%s", returnURL))
	c.Abort()
}

// injectSiteInfo 从 context 注入站点展示信息
func injectSiteInfo(c *gin.Context, data gin.H) {
	if framework, exists := c.Get("site_framework"); exists {
		data["Framework"] = framework
	}
	if copyrightStart, exists := c.Get("site_copyright_start"); exists {
		data["CopyrightStart"] = copyrightStart
	}
}
