package internal

import (
	"bytes"
	"context"
	"filesdk"
	"fmt"
	"io"
	"net/http"
	"net/url"
	"os"
	"path/filepath"
	"strings"

	"github.com/gogf/gf/v2/os/glog"
	"yx-dataset-server/app/schema"
	"yx-dataset-server/library/logger"
	"yx-dataset-server/library/minio"
	"yx-dataset-server/library/utils"
)

// NewFile 创建文件管理实例
func NewFile() *File {
	return &File{}
}

// File 文件管理
type File struct{}

// Upload 上传文件
func (a *File) Upload(ctx context.Context, r *http.Request, formKey, bucket string) (*schema.FileInfo, error) {
	file, header, err := r.FormFile(formKey)
	if err != nil {
		return nil, err
	}
	buff := new(bytes.Buffer)
	_, err = io.Copy(buff, file)
	if err != nil {
		return nil, err
	}
	headers := map[string]string{}
	headers["FILE-EXPIRE"] = utils.GetConfig("file_server.file-expire").String()
	result, err := filesdk.GetHandle().Upload(header.Filename, bucket, headers, buff.Bytes())
	if err != nil {
		glog.Debugf(ctx, "上传文件失败:%s", err.Error())
		return nil, err
	}
	err = filesdk.GetHandle().HandlePersistent(result.Hash)
	if err != nil {
		logger.Printf(ctx, "文件持久化设置失败：%s", err.Error())
	}

	info := &schema.FileInfo{
		URL:  result.URL,
		Name: result.Name,
		Size: result.Size,
		MD5:  result.Hash,
	}

	return info, nil
}

// Download 下载文件
func (a *File) Download(ctx context.Context, fileUrl string) ([]byte, string, error) {
	Url, err := url.PathUnescape(fileUrl)
	if err != nil {
		return nil, "", err
	}
	stat, err := minio.GetClient().Stat(Url)
	if err != nil {
		fmt.Println("文件不存在")
		return nil, "", err
	}
	fileData, err := a.getSourceFileData(ctx, Url)
	if err != nil {
		return nil, "", err
	}

	return fileData, stat.ContentType, nil

}

func (a *File) getSourceFileData(ctx context.Context, file string) ([]byte, error) {
	var fileData []byte
	obj, err := minio.GetClient().Get(ctx, file)
	if err != nil {
		return nil, err
	}
	buf := new(bytes.Buffer)
	_, _ = io.Copy(buf, obj)

	_ = obj.Close()
	fileData = buf.Bytes()
	return fileData, nil
}

// 修正文件名，将半角 % 替换为全角 ％（不替换的话文件将无法从浏览器中打开）
func (a *File) getFileName(fileName string) string {
	return strings.ReplaceAll(fileName, "%", "％")
}

func (a *File) UploadFromTrainFolder(ctx context.Context, fileName, bucket string) (*schema.FileInfo, error) {
	// 1. 固定目标文件夹路径（和你的目录结构完全匹配）
	// 绝对路径兜底，避免程序运行目录错乱
	baseDir := "/opt/go/yx-dataset-server/file/产品知识"
	// 安全拼接完整路径，防止路径穿越漏洞
	fullFilePath := filepath.Join(baseDir, fileName)

	// 2. 打开本地文件（替代原 r.FormFile 表单取文件逻辑）
	localFile, err := os.Open(fullFilePath)
	if err != nil {
		return nil, fmt.Errorf("读取产品知识文件失败: %w", err)
	}
	// 函数结束自动关闭文件，防止句柄泄漏
	defer localFile.Close()

	// 3. 读取文件到缓冲区（完全保留你原有的内存拷贝逻辑）
	buff := new(bytes.Buffer)
	_, err = io.Copy(buff, localFile)
	if err != nil {
		return nil, fmt.Errorf("文件读取缓存失败: %w", err)
	}

	// 4. 原有SDK上传逻辑1:1保留
	headers := map[string]string{}
	headers["FILE-EXPIRE"] = utils.GetConfig("file_server.file-expire").String()

	result, err := filesdk.GetHandle().Upload(fileName, bucket, headers, buff.Bytes())
	if err != nil {
		glog.Debugf(ctx, "上传文件失败:%s", err.Error())
		return nil, err
	}

	// 5. 文件持久化逻辑完整保留
	err = filesdk.GetHandle().HandlePersistent(result.Hash)
	if err != nil {
		logger.Printf(ctx, "文件持久化设置失败：%s", err.Error())
	}

	// 6. 返回结构完全兼容原定义
	info := &schema.FileInfo{
		URL:  result.URL,
		Name: result.Name,
		Size: result.Size,
		MD5:  result.Hash,
	}

	return info, nil
}

func (a *File) UploadAllTrainFiles(ctx context.Context, path, bucket string) ([]*schema.FileInfo, error) {
	baseDir := fmt.Sprintf("/opt/go/yx-dataset-server/file/%s", path)

	fmt.Printf(fmt.Sprintf("正在读取【%s】目录下的所有文件...\n", path))
	// 读取目录内所有文件
	dirEntries, err := os.ReadDir(baseDir)
	if err != nil {
		return nil, fmt.Errorf("读取文件失败: %w", err)
	}

	var uploadResult []*schema.FileInfo
	fmt.Printf("开始上传文件...\n")
	for k, entry := range dirEntries {
		if entry.IsDir() {
			continue
		}
		fmt.Printf("正在处理第%d个文件：%s\n", k, entry.Name())
		// 调用上面的单文件上传方法
		fileInfo, err := a.UploadFromTrainFolder(ctx, entry.Name(), bucket)
		if err != nil {
			glog.Debugf(ctx, "文件【%s】上传跳过: %s", entry.Name(), err.Error())
			continue
		}

		uploadResult = append(uploadResult, fileInfo)
	}

	return uploadResult, nil
}

// UploadFromLocal 从本地文件路径上传文件
func (a *File) UploadFromLocal(ctx context.Context, filePath string, bucket string) (*schema.FileInfo, error) {
	logger.Printf(ctx, "[文件上传] 开始上传本地文件: %s, bucket: %s", filePath, bucket)

	file, err := os.Open(filePath)
	if err != nil {
		logger.Printf(ctx, "[文件上传] 打开文件失败: %s, 错误: %s", filePath, err.Error())
		return nil, fmt.Errorf("打开文件失败: %s", err.Error())
	}
	defer file.Close()

	// 获取文件信息
	fileInfo, err := file.Stat()
	if err != nil {
		logger.Printf(ctx, "[文件上传] 获取文件信息失败: %s, 错误: %s", filePath, err.Error())
		return nil, fmt.Errorf("获取文件信息失败: %s", err.Error())
	}

	fileSize := fileInfo.Size()
	fileName := fileInfo.Name()
	logger.Printf(ctx, "[文件上传] 文件信息 - 名称: %s, 大小: %d bytes", fileName, fileSize)

	// 读取文件内容
	data, err := io.ReadAll(file)
	if err != nil {
		logger.Printf(ctx, "[文件上传] 读取文件内容失败: %s, 错误: %s", filePath, err.Error())
		return nil, fmt.Errorf("读取文件内容失败: %s", err.Error())
	}

	logger.Printf(ctx, "[文件上传] 文件读取完成: %s, 实际读取: %d bytes", filePath, len(data))

	headers := map[string]string{}
	headers["FILE-EXPIRE"] = utils.GetConfig("file_server.file-expire").String()

	logger.Printf(ctx, "[文件上传] 开始上传到Minio: %s", filePath)
	result, err := filesdk.GetHandle().Upload(fileName, bucket, headers, data)
	if err != nil {
		logger.Printf(ctx, "[文件上传] Minio上传失败: %s, 错误: %s", filePath, err.Error())
		glog.Debugf(ctx, "上传文件失败: %s", err.Error())
		return nil, err
	}

	logger.Printf(ctx, "[文件上传] Minio上传成功: %s -> %s, MD5: %s", filePath, result.URL, result.Hash)

	err = filesdk.GetHandle().HandlePersistent(result.Hash)
	if err != nil {
		logger.Printf(ctx, "[文件上传] 文件持久化设置失败: %s, 错误: %s", filePath, err.Error())
	}

	info := &schema.FileInfo{
		URL:  result.URL,
		Name: result.Name,
		Size: result.Size,
		MD5:  result.Hash,
	}

	logger.Printf(ctx, "[文件上传] 上传完成: %s", filePath)
	return info, nil
}
