package _123_open

import (
	"context"
	"fmt"
	"strconv"
	"time"

	"github.com/OpenListTeam/OpenList/v4/internal/driver"
	"github.com/OpenListTeam/OpenList/v4/internal/errs"
	"github.com/OpenListTeam/OpenList/v4/internal/model"
	"github.com/OpenListTeam/OpenList/v4/internal/op"
	"github.com/OpenListTeam/OpenList/v4/internal/stream"
	"github.com/OpenListTeam/OpenList/v4/pkg/utils"
)

type Open123 struct {
	model.Storage
	Addition
	UID uint64
}

func (d *Open123) Config() driver.Config {
	return config
}

func (d *Open123) GetAddition() driver.Additional {
	return &d.Addition
}

func (d *Open123) Init(ctx context.Context) error {
	if d.UploadThread < 1 || d.UploadThread > 32 {
		d.UploadThread = 3
	}

	return nil
}

func (d *Open123) Drop(ctx context.Context) error {
	op.MustSaveDriverStorage(d)
	return nil
}

func (d *Open123) List(ctx context.Context, dir model.Obj, args model.ListArgs) ([]model.Obj, error) {
	fileLastId := int64(0)
	parentFileId, err := strconv.ParseInt(dir.GetID(), 10, 64)
	if err != nil {
		return nil, err
	}
	res := make([]File, 0)

	for fileLastId != -1 {
		files, err := d.getFiles(parentFileId, 100, fileLastId)
		if err != nil {
			return nil, err
		}
		// 目前123panAPI请求，trashed失效，只能通过遍历过滤
		for i := range files.Data.FileList {
			if files.Data.FileList[i].Trashed == 0 {
				res = append(res, files.Data.FileList[i])
			}
		}
		fileLastId = files.Data.LastFileId
	}
	return utils.SliceConvert(res, func(src File) (model.Obj, error) {
		return src, nil
	})
}

func (d *Open123) Link(ctx context.Context, file model.Obj, args model.LinkArgs) (*model.Link, error) {
	fileId, _ := strconv.ParseInt(file.GetID(), 10, 64)

	if d.DirectLink {
		res, err := d.getDirectLink(fileId)
		if err != nil {
			return nil, err
		}

		if d.DirectLinkPrivateKey == "" {
			duration := 365 * 24 * time.Hour // 缓存1年
			return &model.Link{
				URL:        res.Data.URL,
				Expiration: &duration,
			}, nil
		}

		uid, err := d.getUID(ctx)
		if err != nil {
			return nil, err
		}

		duration := time.Duration(d.DirectLinkValidDuration) * time.Minute

		newURL, err := d.SignURL(res.Data.URL, d.DirectLinkPrivateKey,
			uid, duration)
		if err != nil {
			return nil, err
		}

		return &model.Link{
			URL:        newURL,
			Expiration: &duration,
		}, nil
	}

	res, err := d.getDownloadInfo(fileId)
	if err != nil {
		return nil, err
	}

	return &model.Link{URL: res.Data.DownloadUrl}, nil
}

func (d *Open123) MakeDir(ctx context.Context, parentDir model.Obj, dirName string) error {
	parentFileId, _ := strconv.ParseInt(parentDir.GetID(), 10, 64)

	return d.mkdir(parentFileId, dirName)
}

func (d *Open123) Move(ctx context.Context, srcObj, dstDir model.Obj) error {
	toParentFileID, _ := strconv.ParseInt(dstDir.GetID(), 10, 64)

	return d.move(srcObj.(File).FileId, toParentFileID)
}

func (d *Open123) Rename(ctx context.Context, srcObj model.Obj, newName string) error {
	fileId, _ := strconv.ParseInt(srcObj.GetID(), 10, 64)

	return d.rename(fileId, newName)
}

func (d *Open123) Copy(ctx context.Context, srcObj, dstDir model.Obj) error {
	// 尝试使用上传+MD5秒传功能实现复制
	// 1. 创建文件
	// parentFileID 父目录id，上传到根目录时填写 0
	parentFileId, err := strconv.ParseInt(dstDir.GetID(), 10, 64)
	if err != nil {
		return fmt.Errorf("parse parentFileID error: %v", err)
	}
	etag := srcObj.(File).Etag
	createResp, err := d.create(parentFileId, srcObj.GetName(), etag, srcObj.GetSize(), 2, false)
	if err != nil {
		return err
	}
	// 是否秒传
	if createResp.Data.Reuse {
		return nil
	}
	return errs.NotSupport
}

func (d *Open123) Remove(ctx context.Context, obj model.Obj) error {
	fileId, _ := strconv.ParseInt(obj.GetID(), 10, 64)

	return d.trash(fileId)
}

func (d *Open123) Put(ctx context.Context, dstDir model.Obj, file model.FileStreamer, up driver.UpdateProgress) (model.Obj, error) {
	// 1. 创建文件
	// parentFileID 父目录id，上传到根目录时填写 0
	parentFileId, err := strconv.ParseInt(dstDir.GetID(), 10, 64)
	if err != nil {
		return nil, fmt.Errorf("parse parentFileID error: %v", err)
	}
	// etag 文件md5
	etag := file.GetHash().GetHash(utils.MD5)
	if len(etag) < utils.MD5.Width {
		_, etag, err = stream.CacheFullAndHash(file, &up, utils.MD5)
		if err != nil {
			return nil, err
		}
	}
	createResp, err := d.create(parentFileId, file.GetName(), etag, file.GetSize(), 2, false)
	if err != nil {
		return nil, err
	}
	// 是否秒传
	if createResp.Data.Reuse {
		// 秒传成功才会返回正确的 FileID，否则为 0
		if createResp.Data.FileID != 0 {
			return File{
				FileName: file.GetName(),
				Size:     file.GetSize(),
				FileId:   createResp.Data.FileID,
				Type:     2,
				Etag:     etag,
			}, nil
		}
	}

	// 2. 上传分片
	err = d.Upload(ctx, file, createResp, up)
	if err != nil {
		return nil, err
	}

	// 3. 上传完毕
	for range 60 {
		uploadCompleteResp, err := d.complete(createResp.Data.PreuploadID)
		// 返回错误代码未知，如：20103，文档也没有具体说
		if err == nil && uploadCompleteResp.Data.Completed && uploadCompleteResp.Data.FileID != 0 {
			up(100)
			return File{
				FileName: file.GetName(),
				Size:     file.GetSize(),
				FileId:   uploadCompleteResp.Data.FileID,
				Type:     2,
				Etag:     etag,
			}, nil
		}
		// 若接口返回的completed为 false 时，则需间隔1秒继续轮询此接口，获取上传最终结果。
		time.Sleep(time.Second)
	}
	return nil, fmt.Errorf("upload complete timeout")
}

func (d *Open123) GetDetails(ctx context.Context) (*model.StorageDetails, error) {
	userInfo, err := d.getUserInfo(ctx)
	if err != nil {
		return nil, err
	}
	total := userInfo.Data.SpacePermanent + userInfo.Data.SpaceTemp
	free := total - userInfo.Data.SpaceUsed
	return &model.StorageDetails{
		DiskUsage: model.DiskUsage{
			TotalSpace: total,
			FreeSpace:  free,
		},
	}, nil
}

func (d *Open123) OfflineDownload(ctx context.Context, url string, dir model.Obj, callback string) (int, error) {
	return d.createOfflineDownloadTask(ctx, url, dir.GetID(), callback)
}

func (d *Open123) OfflineDownloadProcess(ctx context.Context, taskID int) (float64, int, error) {
	return d.queryOfflineDownloadStatus(ctx, taskID)
}

var (
	_ driver.Driver    = (*Open123)(nil)
	_ driver.PutResult = (*Open123)(nil)
)
