package util

import (
	"bufio"
	"context"
	"crypto/rand"
	"encoding/hex"
	"fmt"
	"io"
	"net/http"
	"net/url"
	"os"
	"path/filepath"
	"strings"
	"sync"
	"time"
	"unicode"

	"log"

	"github.com/google/uuid"
)

var (
	rootDir     string
	rootDirErr  error
	rootDirOnce sync.Once
)

// 支持的图片扩展名（小写）
var imageExts = map[string]struct{}{
	".jpg":  {},
	".jpeg": {},
	".png":  {},
	".gif":  {},
	".bmp":  {},
	".webp": {},
	".tiff": {},
	".svg":  {},
}

// GenerateRandomString generates a random string of the specified length.
func GenerateRandomString(length int) (string, error) {
	bytes := make([]byte, length)
	_, err := rand.Read(bytes)
	if err != nil {
		return "", err
	}
	return hex.EncodeToString(bytes)[:length], nil
}

// IsEmpty checks if a string is empty.
func IsEmpty(s string) bool {
	return len(s) == 0
}

// TrimString trims whitespace from the beginning and end of a string.
func TrimString(s string) string {
	return strings.TrimSpace(s)
}

// SplitString 切割字符串，支持空格和中文逗号
func SplitTags(s string) []string {
	// 使用 strings.FieldsFunc 分割字符串
	fields := strings.FieldsFunc(s, func(r rune) bool {
		return unicode.IsSpace(r) || r == '，'
	})

	return fields
}

func SplitTags2(s string) string {
	return strings.Join(SplitTags(s), ",")
}

func getRootDir() (string, error) {
	rootDirOnce.Do(func() {
		rootDir, rootDirErr = os.Getwd()
	})
	return rootDir, rootDirErr
}

// SaveImageFromURL 保存 URL 图片到本地
func SaveImageFromURL(imgURL, filePartPath string) (filePath string, err error) {
	if strings.HasPrefix(filePartPath, "http") {
		return "", fmt.Errorf("保存路径有误")
	}
	// 1. 获取项目根目录 (只获取一次)
	rootDir, err := getRootDir()
	if err != nil {
		return "", fmt.Errorf("获取项目根目录失败: %w", err)
	}

	// 3. 拼接完整文件路径
	fileVirPath := filepath.Join(filePartPath, GetImageNameFromURL(imgURL))
	fileAbsPath := filepath.Join(rootDir, fileVirPath)

	// 4. 创建 media 文件夹（如果不存在）
	if err = os.MkdirAll(filepath.Dir(fileAbsPath), 0755); err != nil {
		return "", fmt.Errorf("创建 media 目录失败: %w", err)
	}

	// 5. 创建目标文件
	file, err := os.Create(fileAbsPath)
	if err != nil {
		return "", fmt.Errorf("创建文件失败: %w", err)
	}
	defer func() {
		if closeErr := file.Close(); closeErr != nil {
			log.Printf("关闭文件失败: %v", closeErr)
		}
	}()

	// 6. 发起 HTTP 请求
	client := &http.Client{
		Timeout: 60 * time.Second, // 设置超时时间
	}
	req, err := http.NewRequest("GET", imgURL, nil)
	if err != nil {
		return "", fmt.Errorf("创建 HTTP 请求失败: %w", err)
	}

	// 使用 context 增加超时控制
	ctx, cancel := context.WithTimeout(context.Background(), 60*time.Second)
	defer cancel()

	req = req.WithContext(ctx)

	resp, err := client.Do(req)
	if err != nil {
		return "", fmt.Errorf("HTTP 请求失败: %w", err)
	}
	defer func() {
		if closeErr := resp.Body.Close(); closeErr != nil {
			log.Printf("关闭响应 Body 失败: %v", closeErr)
		}
	}()

	// 7. 检查 HTTP 状态码
	if resp.StatusCode != http.StatusOK {
		return "", fmt.Errorf("HTTP 状态码错误: %d", resp.StatusCode)
	}

	// 8. 使用 io.CopyBuffer 高效复制内容
	buffer := make([]byte, 32768) // 32KB 缓冲区
	_, err = io.CopyBuffer(file, resp.Body, buffer)
	if err != nil {
		return "", fmt.Errorf("复制内容失败: %w", err)
	}

	return fileVirPath, nil
}

// GenerateUUIDFileName 通过 URL 获取文件后缀并生成带 UUID 的文件路径
func GenerateUUIDFileName(fileURL string) string {
	// 1. 解析 URL
	parsedURL, err := url.Parse(fileURL)
	if err != nil {
		return ""
	}

	// 2. 获取路径
	path := parsedURL.Path

	// 3. 获取文件后缀
	ext := filepath.Ext(path)

	// 4. 如果没有后缀，则默认为 .html
	if ext == "" {
		ext = ".png"
	}

	// 5. 生成随机 UUID 文件名
	return fmt.Sprintf("%s%s", uuid.New().String(), ext)
}

// GenerateUUID2FileName 生成随机 UUID 文件名
func GenerateUUID2FileName(ext string, hasLine bool) string {
	// 1. 生成 UUID
	uuid := uuid.New().String()

	if !hasLine {
		return fmt.Sprintf("%s%s", strings.ReplaceAll(uuid, "-", ""), ext)
	}

	// 2. 拼接文件名
	return fmt.Sprintf("%s%s", uuid, ext)
}

// GetImageNameFromURL 从给定的图片URL中提取出图片的文件名。
// 这个函数主要用于处理和解析图片URL，以便获取图片的文件名部分。
// 参数:
//
//	imageURL - 图片的URL地址，字符串类型。
//
// 返回值:
//
//	返回图片的文件名，如果无法解析或URL格式不正确，则返回空字符串。
func GetImageNameFromURL(imageURL string) string {
	parts := strings.Split(imageURL, "/")
	if len(parts) == 0 {
		return ""
	}
	lastPart := parts[len(parts)-1]
	if lastPart == "" {
		return ""
	}
	return filepath.Base(lastPart)
}

// 图片处理器
type ImageProcessor struct {
	srcDir     string         // 源目录
	dstDir     string         // 目标目录
	fileExists map[string]int // 记录已存在的文件名及冲突次数
	mu         sync.Mutex     // 保护fileExists
	wg         sync.WaitGroup // 等待所有goroutine完成
}

// 新建图片处理器
func NewImageProcessor(srcDir string) *ImageProcessor {
	return &ImageProcessor{
		srcDir:     srcDir,
		dstDir:     filepath.Join(srcDir, "merge"),
		fileExists: make(map[string]int),
	}
}

// 检查文件是否为图片
func (p *ImageProcessor) isImage(path string) bool {
	ext := strings.ToLower(filepath.Ext(path))
	_, ok := imageExts[ext]
	return ok
}

// 处理单个图片文件
func (p *ImageProcessor) processImage(srcPath string) {
	defer p.wg.Done()

	// 获取文件名（带扩展名）
	baseName := filepath.Base(srcPath)

	// 清理掉文件名不符合规范的字符
	titleClear := NewTitleCleaner()
	baseName = titleClear.CleanForFileName(baseName)

	// 生成目标路径（处理重名）
	dstPath := p.generateDstPath(baseName)

	// 创建目标目录
	dstDir := filepath.Dir(dstPath)
	if err := os.MkdirAll(dstDir, 0755); err != nil {
		fmt.Printf("错误：无法创建目录 %s: %v\n", dstDir, err)
		return
	}

	// 直接复制文件，不再尝试硬链接
	if err := p.copyFile(srcPath, dstPath); err != nil {
		fmt.Printf("错误：无法复制文件 %s 到 %s: %v\n", srcPath, dstPath, err)
		// 删除可能创建的不完整文件
		if _, statErr := os.Stat(dstPath); statErr == nil {
			if removeErr := os.Remove(dstPath); removeErr != nil {
				fmt.Printf("警告：无法删除不完整文件 %s: %v\n", dstPath, removeErr)
			}
		}
	}
}

// 复制文件（使用缓冲区提升性能）
func (p *ImageProcessor) copyFile(src, dst string) error {
	srcFile, err := os.Open(src)
	if err != nil {
		return err
	}
	defer srcFile.Close()

	dstFile, err := os.Create(dst)
	if err != nil {
		return err
	}
	defer dstFile.Close()

	// 使用带缓冲区的复制（默认缓冲区为120KB）
	buf := make([]byte, 120*1024)
	writer := bufio.NewWriterSize(dstFile, 120*1024)
	reader := bufio.NewReaderSize(srcFile, 120*1024)
	written, err := io.CopyBuffer(writer, reader, buf)
	if err != nil {
		return err
	}
	if err = writer.Flush(); err != nil {
		return err
	}

	// 强制刷新缓冲区到磁盘
	if err = dstFile.Sync(); err != nil {
		return fmt.Errorf("无法刷新文件缓冲区: %v", err)
	}

	// 验证文件大小是否一致
	srcInfo, err := srcFile.Stat()
	if err != nil {
		return fmt.Errorf("无法获取源文件大小: %v", err)
	}
	if written != srcInfo.Size() {
		return fmt.Errorf("文件复制不完整，预期大小: %d, 实际大小: %d", srcInfo.Size(), written)
	}
	return nil
}

// 生成目标路径（处理重名）
func (p *ImageProcessor) generateDstPath(baseName string) string {
	p.mu.Lock()
	defer p.mu.Unlock()

	// 分离文件名和扩展名
	ext := filepath.Ext(baseName)
	name := strings.TrimSuffix(baseName, ext)

	// 检查基础名称是否已存在
	count, exists := p.fileExists[name]

	if exists {
		// 已存在，增加计数器
		count++
		p.fileExists[name] = count
		// 生成新文件名：name_idx.ext
		newName := fmt.Sprintf("%s_%d%s", name, count, ext)
		return filepath.Join(p.dstDir, newName)
	} else {
		// 不存在，记录并返回原始名称
		p.fileExists[name] = 0
		return filepath.Join(p.dstDir, baseName)
	}
}

// 执行合并操作
func (p *ImageProcessor) Merge() (string, error) {
	// 创建merge目录
	if err := os.MkdirAll(p.dstDir, 0755); err != nil {
		return "", fmt.Errorf("无法创建merge目录: %v", err)
	}

	startTime := time.Now()
	fmt.Printf("开始合并分散图片...\n源目录: %s\n目标目录: %s\n", p.srcDir, p.dstDir)

	// 递归遍历目录
	err := filepath.Walk(p.srcDir, func(path string, info os.FileInfo, err error) error {
		if err != nil {
			return err
		}

		// 跳过media、merge目录及merge目标目录
		if info.IsDir() && (strings.Contains(path, "media") || strings.Contains(path, "merge") || path == p.dstDir) {
			return filepath.SkipDir
		}

		// 如果是图片文件，启动goroutine处理
		if !info.IsDir() && p.isImage(path) {
			p.wg.Add(1)
			go p.processImage(path)
		}

		return nil
	})

	if err != nil {
		return "", fmt.Errorf("遍历目录时出错: %v", err)
	}

	// 等待所有goroutine完成
	p.wg.Wait()

	elapsed := time.Since(startTime)
	fmt.Printf("完成合并分散图片！共处理 %d 个图片文件，耗时: %v\n", len(p.fileExists), elapsed)
	return p.dstDir, nil
}

// 复制文件（使用缓冲区提升性能）
func CopyFile(srcFileName, dstFileName string) error {
	// 创建目标文件所在目录
	dstDir := filepath.Dir(dstFileName)
	if err := os.MkdirAll(dstDir, 0755); err != nil {
		return fmt.Errorf("无法创建目标目录: %v", err)
	}

	srcFile, err := os.Open(srcFileName)
	if err != nil {
		return err
	}
	defer srcFile.Close()

	dstFile, err := os.Create(dstFileName)
	if err != nil {
		return err
	}
	defer dstFile.Close()

	// 使用带缓冲区的复制（默认缓冲区为120KB）
	buf := make([]byte, 120*1024)
	writer := bufio.NewWriterSize(dstFile, 120*1024)
	reader := bufio.NewReaderSize(srcFile, 120*1024)
	written, err := io.CopyBuffer(writer, reader, buf)
	if err != nil {
		return err
	}

	if err = writer.Flush(); err != nil {
		return err
	}

	// 强制刷新缓冲区到磁盘
	if err = dstFile.Sync(); err != nil {
		return fmt.Errorf("无法刷新文件缓冲区: %v", err)
	}

	// 验证文件大小是否一致
	srcInfo, err := srcFile.Stat()
	if err != nil {
		return fmt.Errorf("无法获取源文件大小: %v", err)
	}
	if written != srcInfo.Size() {
		return fmt.Errorf("文件复制不完整，预期大小: %d, 实际大小: %d", srcInfo.Size(), written)
	}
	return nil
}
