package services

import (
	"bufio"
	"bytes"
	"fmt"
	"io"
	"mime/multipart"
	"net/http"
	"os"
	"strconv"
	"strings"

	"nms_nbi/lib/log"
)

const (
	RootPath      = "uploads/"
	ChunkRootPath = "uploads_tmp/"
)

var (
	// FilesMax 限制上传文件的大小为7 MB
	FilesMax int64 = 32 << 20
	// ValuesMax 限制POST字段内容的大小
	ValuesMax int64 = 512
)

func GetPostFile(w http.ResponseWriter, r *http.Request) {
	//获取文件流,第三个返回值是错误对象
	file, header, _ := r.FormFile("file")
	//读取文件流为[]byte
	b, err := io.ReadAll(file)
	if err != nil {
		log.Error("Failed to ReadAll:", err)
		ResponseInternalServerError500ProcessError(w, err)
		return
	}
	//把文件保存到指定位置
	err = os.WriteFile("./upload/test.zip", b, 0644)
	if err != nil {
		log.Error("Failed to WriteFile:", err)
		ResponseInternalServerError500ProcessError(w, err)
		return
	}
	//输出上传时文件名
	log.Debug("filename:", header.Filename)
}

func GetUploadFile(w http.ResponseWriter, r *http.Request) {
	log.Debug("GetUploadFile processing...")

	file, err := os.Create("./test.zip")
	if err != nil {
		log.Error("Failed to Create:", err)
		ResponseInternalServerError500ProcessError(w, err)
		return
	}
	_, err = io.Copy(file, r.Body)
	if err != nil {
		log.Error("Failed to Copy:", err)
		ResponseInternalServerError500ProcessError(w, err)
		return
	}
}

func GetUploadFormFile(w http.ResponseWriter, r *http.Request) {
	// 设置最大的内存限制为32MB
	r.ParseMultipartForm(32 << 20)
	file, handler, err := r.FormFile("file")
	if err != nil {
		log.Error("Failed to FormFile:", err)
		ResponseInternalServerError500ProcessError(w, err)
		return
	}
	defer file.Close()
	log.Debug("Header:%v", handler.Header)
	f, err := os.OpenFile("./"+handler.Filename, os.O_WRONLY|os.O_CREATE, 0666)
	if err != nil {
		log.Error("Failed to OpenFile:", err)
		ResponseInternalServerError500ProcessError(w, err)
		return
	}
	defer f.Close()
	_, err = io.Copy(f, file)
	if err != nil {
		log.Error("Failed to Copy:", err)
		ResponseInternalServerError500ProcessError(w, err)
		return
	}
	log.Debug("File uploaded successfully:", handler.Filename)
}

func HandleUploadFile(r *http.Request, path, newFileName string) (string, error) {
	var filePath, fileName string
	reader, err := r.MultipartReader()
	if err != nil {
		log.Error("Failed to MultipartReader:", err)
		return "", err
	}

	for {
		part, err := reader.NextPart()
		if err == io.EOF {
			break
		} else if err != nil {
			log.Error("Failed to NextPart:", err)
			return "", err
		}

		log.Debugf("FileName=[%s], FormName=[%s]", part.FileName(), part.FormName())
		if part.FileName() == "" { // this is FormData
			data, _ := io.ReadAll(part)
			log.Debugf("FormData=[%s]", string(data))
		} else { // This is FileData

			if newFileName != "" {
				fileName = newFileName
			} else {
				fileName = part.FileName()
			}

			err := os.MkdirAll(path, os.ModePerm)
			if err != nil {
				log.Error("Failed to Mkdir:", err)
				return "", err
			}

			filePath = path + "/" + fileName

			file, err := os.Create(filePath)
			if err != nil {
				log.Error("Failed to Create:", err)
				return "", err
			}
			defer file.Close()
			_, err = io.Copy(file, part)
			if err != nil {
				log.Error("Failed to Copy:", err)
				return "", err
			}
		}
	}
	return fileName, nil
}

type UploadMultiFileData struct {
	SoftwareFileName string              `json:"softwareFileName"`
	CmsFileName      string              `json:"cmsFileName"`
	Datas            map[string][]string `json:"datas"`
}

func HandleUploadMultiFile(r *http.Request, path, newFileName string) (*UploadMultiFileData, error) {
	fileData := new(UploadMultiFileData)
	// 解析multipart/form-data请求
	err := r.ParseMultipartForm(100 << 20) // 100MB
	if err != nil {
		return fileData, err
	}

	// 获取文件和数据
	softwareFile := r.MultipartForm.File["file"]
	cmsFile := r.MultipartForm.File["cms"]
	fileData.Datas = r.MultipartForm.Value

	// 处理文件
	if len(softwareFile) > 0 {
		file := softwareFile[0]
		// 打开文件
		f, err := file.Open()
		if err != nil {
			return fileData, err
		}
		defer f.Close()

		// 创建本地文件
		dst, err := os.Create(path + "/" + file.Filename)
		if err != nil {
			return fileData, err
		}
		defer dst.Close()

		fileData.SoftwareFileName = file.Filename
		// 将文件内容拷贝到本地文件
		_, err = io.Copy(dst, f)
		if err != nil {
			return fileData, err
		}
	}
	// 处理文件
	if len(cmsFile) > 0 {
		file := cmsFile[0]
		// 打开文件
		f, err := file.Open()
		if err != nil {
			return fileData, err
		}
		defer f.Close()

		// 创建本地文件
		dst, err := os.Create(path + "/" + file.Filename)
		if err != nil {
			return fileData, err
		}
		defer dst.Close()

		fileData.CmsFileName = file.Filename
		// 将文件内容拷贝到本地文件
		_, err = io.Copy(dst, f)
		if err != nil {
			return fileData, err
		}
	}

	return fileData, nil
}

func HandleUploadFormFile(w http.ResponseWriter, r *http.Request) {
	r.ParseMultipartForm(32 << 20)
	//mForm := r.MultipartForm

	for k := range r.MultipartForm.File {
		// k is the key of file part
		file, fileHeader, err := r.FormFile(k)
		if err != nil {
			fmt.Println("inovke FormFile error:", err)
			return
		}
		defer file.Close()
		fmt.Printf("the uploaded file: name[%s], size[%d], header[%#v]\n",
			fileHeader.Filename, fileHeader.Size, fileHeader.Header)

		// store uploaded file into local path
		localFileName := "./upload/" + fileHeader.Filename
		out, err := os.Create(localFileName)
		if err != nil {
			fmt.Printf("failed to open the file %s for writing", localFileName)
			return
		}
		defer out.Close()
		_, err = io.Copy(out, file)
		if err != nil {
			fmt.Printf("copy file err:%s\n", err)
			return
		}
		fmt.Printf("file %s uploaded ok\n", fileHeader.Filename)
	}
}

func PostFileHandler(w http.ResponseWriter, r *http.Request) {
	fmt.Println("PostFileHandler processing... ")
	if !strings.Contains(r.Header.Get("Content-Type"), "multipart/form-data") {
		// 不支持的 Content-Type 类型
		fmt.Println("Invalid Content-Type: ", r.Header.Get("Content-Type"))
		http.Error(w, " Unsupported Content-Type Types", http.StatusBadRequest)
		return
	}

	// 整个请求的主体大小设置为7.5Mb
	r.Body = http.MaxBytesReader(w, r.Body, FilesMax+ValuesMax)
	reader, err := r.MultipartReader()
	if err != nil {
		fmt.Println(err)
		http.Error(w, err.Error(), http.StatusBadRequest)
		return
	}

	for {
		// A Part represents a single part in a multipart body.
		part, err := reader.NextPart()
		if err != nil {
			if err == io.EOF {

				break
			}

			http.Error(w, err.Error(), http.StatusInternalServerError)
			return
		}

		fileName := part.FileName()
		formName := part.FormName()
		var buf = &bytes.Buffer{}
		// 非文件字段部分大小限制验证（非文件字段，go中filename会是空）
		if fileName == "" {
			// "请求主体中非文件字段" + formName + "超出大小限制"
			var limitError = fmt.Sprintf("Non-file field %s in request body exceeds size limit", formName)
			err = uploadSizeLimit(buf, part, ValuesMax, limitError)
			if err != nil {
				http.Error(w, err.Error(), http.StatusBadRequest)
				return
			}
			continue
		}

		// 文件字段部分大小限制验证
		// "请求主体中文件字段" + fileName + "超出大小限制"
		var limitError = fmt.Sprintf("File field %s in request body exceeds size limit", fileName)
		err = uploadSizeLimit(buf, part, FilesMax, limitError)
		if err != nil {
			http.Error(w, err.Error(), http.StatusBadRequest)
			return
		}

		// 文件创建部分
		if err := uploadFileHandle(r.Header, fileName, buf); err != nil {
			http.Error(w, err.Error(), http.StatusBadRequest)
			return
		}

		// 非逻辑内容，仅为测试使用
		var chunkNumber = r.Header.Get("chunk-number")
		if chunkNumber == "" {
			// "文件"+fileName+"上传成功"
			http.Error(w, fmt.Sprintf("File %s uploaded successfully", fileName), http.StatusOK)
		} else {
			// "分片文件 %s %s 上传成功"
			http.Error(w, fmt.Sprintf("Sliced file %s %s uploaded successfully.", fileName, chunkNumber), http.StatusOK)
		}
	}
}

// 上传内容大小限制
func uploadSizeLimit(buf *bytes.Buffer, part *multipart.Part, maxLimit int64, limitError string) error {
	n, err := io.CopyN(buf, part, maxLimit+1)
	if err != nil && err != io.EOF {
		fmt.Println("PostFileHandler:", err)
		return err
	}
	maxLimit -= n
	if maxLimit < 0 {
		return fmt.Errorf(limitError)
	}
	return nil
}

// uploadFileHandle handle upload file
func uploadFileHandle(header http.Header, fileName string, buf *bytes.Buffer) error {
	var chunkNumberStr = header.Get("chunk-number")
	// 1.普通文件上传处理
	if chunkNumberStr == "" {
		//创建文件并写入文件内容
		return createFile(RootPath+fileName, buf.Bytes())
	}
	// 2.分片文件上传处理
	//2.1读取分片编号
	chunkNumber, err := strconv.Atoi(chunkNumberStr)
	if err != nil {
		return err
	}
	//2.2创建分片文件并写入分片内容
	if err := createFile(fmt.Sprintf(ChunkRootPath+fileName+"%d.chunk", chunkNumber), buf.Bytes()); err != nil {
		return err
	}
	//2.3确认是否上传完毕
	if header.Get("chunk-final") == "true" {
		//2.4合并文件
		if err := mergeChunkFiles(fileName); err != nil {
			return err
		}
		//2.5删除分片
		for i := 0; ; i++ {
			chunFileName := fmt.Sprintf(ChunkRootPath+fileName+"%d.chunk", i)
			err := os.Remove(chunFileName)
			if err != nil {
				if os.IsNotExist(err) {
					break
				}
				return err
			}
		}
	}
	return nil
}

// 创建文件并写入内容
func createFile(fileName string, res []byte) error {
	newFile, err := os.Create(fileName)
	if err != nil {
		return err
	}
	defer func() {
		_ = newFile.Close()
	}()
	bufferedWriter := bufio.NewWriter(newFile)
	_, err = bufferedWriter.Write(res)
	if err != nil && err != io.EOF {
		return err
	}
	return bufferedWriter.Flush()
}

// 合并分片文件
func mergeChunkFiles(fileName string) error {
	var (
		n   int64
		err error
	)
	finalFile, err := os.Create(RootPath + fileName)
	if err != nil {
		return err
	}
	defer finalFile.Close()
	// 将分片内容写入最终文件
	for i := 0; ; i++ {
		chunFile, err := os.Open(fmt.Sprintf(ChunkRootPath+fileName+"%d.chunk", i))
		if err != nil {
			if os.IsNotExist(err) {
				break
			}
			return err
		}
		n, err = io.Copy(finalFile, chunFile)
		if err != nil {
			return err
		}
		err = chunFile.Close()
		if err != nil {
			return err
		}
		if n < 1 {
			break
		}
	}
	return nil
}
