package task

import (
	"context"
	"encoding/json"
	"fmt"
	"os"
	"path/filepath"
	"time"

	model "github.com/cloudreve/Cloudreve/v3/models"
	"github.com/cloudreve/Cloudreve/v3/pkg/filesystem"
	"github.com/cloudreve/Cloudreve/v3/pkg/util"
)

// CompressTask 文件压缩任务
type CompressTask struct {
	User      *model.User
	TaskModel *model.Task
	TaskProps CompressProps
	Err       *JobError

	zipPath string
}

// CompressProps 压缩任务属性
type CompressProps struct {
	Dirs  []uint `json:"dirs"`
	Files []uint `json:"files"`
	Dst   string `json:"dst"`
}

// Props 获取任务属性
func (job *CompressTask) Props() string {
	res, _ := json.Marshal(job.TaskProps)
	return string(res)
}

// Type 获取任务状态
func (job *CompressTask) Type() int {
	return CompressTaskType
}

// Creator 获取创建者ID
func (job *CompressTask) Creator() uint {
	return job.User.ID
}

// Model 获取任务的数据库模型
func (job *CompressTask) Model() *model.Task {
	return job.TaskModel
}

// SetStatus 设定状态
func (job *CompressTask) SetStatus(status int) {
	job.TaskModel.SetStatus(status)
}

// SetError 设定任务失败信息
func (job *CompressTask) SetError(err *JobError) {
	job.Err = err
	res, _ := json.Marshal(job.Err)
	job.TaskModel.SetError(string(res))

	// 删除压缩文件
	job.removeZipFile()
}

func (job *CompressTask) removeZipFile() {
	if job.zipPath != "" {
		if err := os.Remove(job.zipPath); err != nil {
			util.Log().Warning("Failed to delete temp zip file %q: %s", job.zipPath, err)
		}
	}
}

// SetErrorMsg 设定任务失败信息
func (job *CompressTask) SetErrorMsg(msg string) {
	job.SetError(&JobError{Msg: msg})
}

// GetError 返回任务失败信息
func (job *CompressTask) GetError() *JobError {
	return job.Err
}

// Do 开始执行任务
func (job *CompressTask) Do() {
	// 创建文件系统
	fs, err := filesystem.NewFileSystem(job.User)
	if err != nil {
		job.SetErrorMsg(err.Error())
		return
	}

	util.Log().Debug("Starting compress file...")
	job.TaskModel.SetProgress(CompressingProgress)

	// 创建临时压缩文件
	saveFolder := "compress"
	zipFilePath := filepath.Join(
		util.RelativePath(model.GetSettingByName("temp_path")),
		saveFolder,
		fmt.Sprintf("archive_%d.zip", time.Now().UnixNano()),
	)
	zipFile, err := util.CreatNestedFile(zipFilePath)
	if err != nil {
		util.Log().Warning("%s", err)
		job.SetErrorMsg(err.Error())
		return
	}

	defer zipFile.Close()

	// 开始压缩
	ctx := context.Background()
	err = fs.Compress(ctx, zipFile, job.TaskProps.Dirs, job.TaskProps.Files, false)
	if err != nil {
		job.SetErrorMsg(err.Error())
		return
	}

	job.zipPath = zipFilePath
	zipFile.Close()
	util.Log().Debug("Compressed file saved to %q, start uploading it...", zipFilePath)
	job.TaskModel.SetProgress(TransferringProgress)

	// 上传文件
	err = fs.UploadFromPath(ctx, zipFilePath, job.TaskProps.Dst, 0)
	if err != nil {
		job.SetErrorMsg(err.Error())
		return
	}

	job.removeZipFile()
}

// NewCompressTask 新建压缩任务
func NewCompressTask(user *model.User, dst string, dirs, files []uint) (Job, error) {
	newTask := &CompressTask{
		User: user,
		TaskProps: CompressProps{
			Dirs:  dirs,
			Files: files,
			Dst:   dst,
		},
	}

	record, err := Record(newTask)
	if err != nil {
		return nil, err
	}
	newTask.TaskModel = record

	return newTask, nil
}

// NewCompressTaskFromModel 从数据库记录中恢复压缩任务
func NewCompressTaskFromModel(task *model.Task) (Job, error) {
	user, err := model.GetActiveUserByID(task.UserID)
	if err != nil {
		return nil, err
	}
	newTask := &CompressTask{
		User:      &user,
		TaskModel: task,
	}

	err = json.Unmarshal([]byte(task.Props), &newTask.TaskProps)
	if err != nil {
		return nil, err
	}

	return newTask, nil
}