package api

import (
	"fmt"
	"mesh/conf"
	"mesh/db/model"
	"mesh/log"
	"os"
	"path"
	"strings"
	"time"

	"github.com/gin-gonic/gin"
	"github.com/huaweicloud/huaweicloud-sdk-go-obs/obs"
)

func CreateObsClient() (*obs.ObsClient, error) {
	obsConf := conf.AppConfig.Obs
	return obs.New(obsConf.AccessKeyId, obsConf.SecrateKey, obsConf.Endpoint)
}

// u/xxx/img/up/timps.png
func ServiceObsCreateImagePolicy(c *gin.Context, apictx *ApiSession) (interface{}, error) {
	body := struct {
		Ext string
	}{}

	err := c.ShouldBindJSON(&body)
	if err != nil {
		return nil, NewError("参数解析错误!")
	}
	if len(body.Ext) < 1 {
		return nil, NewError("上传文件的扩展名不能为空")
	}

	fkey := fmt.Sprintf("u/%s/images/uploads/%v.%s", apictx.User.Parent, time.Now().UnixNano(), body.Ext)

	client, err := CreateObsClient()
	if err != nil {
		return nil, NewError("创建ObsClient 失败!")
	}

	defer func() {
		client.Close()
	}()

	// 除key，policy，signature外，表单上传时的其他参数，支持的值：
	// 	acl
	// 	cache-control
	// 	content-type
	// 	content-disposition
	// 	content-encoding
	// 	expires
	bucketName := apictx.Svc.Conf.Obs.Bucket

	result, err := client.CreateBrowserBasedSignature(&obs.CreateBrowserBasedSignatureInput{
		Bucket:  bucketName,
		Key:     fkey,
		Expires: 300,
		FormParams: map[string]string{
			"x-obs-acl": "public-read",
		},
	})

	if err != nil {
		return nil, NewLogWithError(err)
	}

	obsConf := conf.AppConfig.Obs

	out := map[string]interface{}{
		"accessKeyId":  obsConf.AccessKeyId,
		"originPolicy": result.OriginPolicy,
		"policy":       result.Policy,
		"signature":    result.Signature,
		"host":         fmt.Sprintf("//%s.%s", bucketName, obsConf.Endpoint),
		"key":          fkey,
	}
	return out, nil
}

func ServiceObsList(c *gin.Context, apictx *ApiSession) (interface{}, error) {

	prefix := c.Query("prefix")

	if len(prefix) < 1 {
		return nil, NewError("prefix不能为空")
	}

	client, err := CreateObsClient()
	if err != nil {
		return nil, NewError("创建ObsClient 失败!")
	}

	defer func() {
		client.Close()
	}()

	bucketName := apictx.Svc.Conf.Obs.Bucket
	result, err := client.ListObjects(&obs.ListObjectsInput{
		Bucket: bucketName,
		ListObjsInput: obs.ListObjsInput{
			Prefix:    prefix,
			Delimiter: "/",
		},
	})
	if err != nil {
		return nil, err
	}

	fmt.Println("result=>", prefix)
	fmt.Println(result.CommonPrefixes, result.IsTruncated)
	out := []*model.ObsItem{}

	imgExts := map[string]bool{".jpeg": true, ".jpg": true, ".gif": true, ".png": true, ".svg": true, ".bmp": true}

	for k, v := range result.Contents {
		fmt.Println(k, v.Key, v.LastModified, v.Size)
		if prefix == v.Key {
			continue
		}
		isDirSuffix := strings.HasSuffix(v.Key, "/")
		isDir := (isDirSuffix && v.Size == 0)
		isImage := false

		originKey := strings.ToLower(v.Key)
		if !isDir && imgExts[path.Ext(originKey)] {
			isImage = true
		}

		keys := strings.Split(v.Key, "/")
		name := ""
		if isDir {
			name = keys[len(keys)-2]
		} else {
			name = keys[len(keys)-1]
		}
		obsConf := conf.AppConfig.Obs
		url := ""
		if !isDir {
			url = fmt.Sprintf("//%s.%s/%s", bucketName, obsConf.Endpoint, v.Key)
		}

		out = append(out, &model.ObsItem{
			Name:         name,
			Size:         uint64(v.Size),
			IsImage:      isImage,
			Url:          url,
			IsDir:        isDir,
			LastModified: v.LastModified,
		})
	}

	for _, v := range result.CommonPrefixes {
		keys := strings.Split(v, "/")
		name := keys[len(keys)-2]

		out = append(out, &model.ObsItem{
			Name:    name,
			IsImage: false,
			IsDir:   true,
		})
	}

	outMap := map[string]interface{}{
		"list":        out,
		"isTruncated": result.IsTruncated,
	}
	return outMap, nil
}

// 管理后台上传文件
func ServiceObsUploadPolicy(c *gin.Context, apictx *ApiSession) (interface{}, error) {

	body := struct {
		Key string
	}{}

	err := c.ShouldBindJSON(&body)
	if err != nil {
		return nil, NewError("参数解析错误!")
	}
	if len(body.Key) < 1 {
		return nil, NewError("上传文件的key不能为空")
	}
	client, err := CreateObsClient()
	if err != nil {
		return nil, NewError("创建ObsClient 失败!")
	}
	defer func() {
		client.Close()
	}()
	bucketName := apictx.Svc.Conf.Obs.Bucket

	result, err := client.CreateBrowserBasedSignature(&obs.CreateBrowserBasedSignatureInput{
		Bucket:  bucketName,
		Key:     body.Key,
		Expires: 600,
		FormParams: map[string]string{
			"x-obs-acl": "public-read",
		},
	})

	if err != nil {
		return nil, NewLogWithError(err)
	}
	obsConf := conf.AppConfig.Obs
	out := map[string]interface{}{
		"accessKeyId":  obsConf.AccessKeyId,
		"originPolicy": result.OriginPolicy,
		"policy":       result.Policy,
		"signature":    result.Signature,
		"host":         fmt.Sprintf("https://%s.%s", bucketName, obsConf.Endpoint),
		"key":          body.Key,
		"saveType":     conf.SaveType_Obs,
	}
	return out, nil
}

func ServiceObsRemove(c *gin.Context, apictx *ApiSession) (interface{}, error) {

	body := struct {
		Key string
	}{}
	err := c.ShouldBindJSON(&body)
	if err != nil {
		return nil, NewError("参数解析错误!")
	}
	if len(body.Key) < 1 {
		return nil, NewError("文件的key不能为空")
	}

	client, err := CreateObsClient()
	if err != nil {
		return nil, NewError("创建ObsClient 失败!")
	}

	defer func() {
		client.Close()
	}()

	bucketName := apictx.Svc.Conf.Obs.Bucket
	result, err := client.DeleteObject(&obs.DeleteObjectInput{
		Bucket: bucketName,
		Key:    body.Key,
	})

	if err != nil {
		return nil, err
	}

	return result, nil
}

func ServiceObsCreateFolder(c *gin.Context, apictx *ApiSession) (interface{}, error) {
	body := struct {
		Key string
	}{}
	err := c.ShouldBindJSON(&body)
	if err != nil {
		return nil, NewError("参数解析错误!")
	}
	if len(body.Key) < 1 {
		return nil, NewError("文件的key不能为空")
	}

	client, err := CreateObsClient()
	if err != nil {
		return nil, NewError("创建ObsClient 失败!")
	}

	defer func() {
		client.Close()
	}()

	bucketName := apictx.Svc.Conf.Obs.Bucket
	result, err := client.PutObject(&obs.PutObjectInput{
		PutObjectBasicInput: obs.PutObjectBasicInput{
			ObjectOperationInput: obs.ObjectOperationInput{
				Bucket: bucketName,
				Key:    body.Key,
				ACL:    obs.AclPublicRead,
			},
		},
	})

	if err != nil {
		return nil, err
	}

	return result, nil
}

func UploadFile(obsClient *obs.ObsClient, bucketName string, fpath string, obsDir string) *model.OssType {

	_, obsname := path.Split(fpath)
	keyFormat := "%s/%s"
	if strings.HasSuffix(obsDir, "/") {
		keyFormat = "%s%s"
	}
	obsKey := fmt.Sprintf(keyFormat, obsDir, obsname)

	input := &obs.PutFileInput{}
	input.Bucket = bucketName
	input.Key = obsKey
	input.SourceFile = fpath

	fpathExt := path.Ext(fpath)
	isGzFile := fpathExt == ".gz"
	if isGzFile {
		input.ContentType = "text/plain"
		input.Metadata = map[string]string{"Content-Encoding": "gzip"}
	}
	result, err := obsClient.PutFile(input)

	isUploadOk := true

	if err != nil {
		log.Errorf("upload obs fail %s", fpath)
		log.Error(err)
		isUploadOk = false
	}

	if result.StatusCode != 200 {
		isUploadOk = false
	}

	if !isUploadOk {
		result, err = obsClient.PutFile(input)

		if err != nil {
			log.Errorf("upload obs fail2 %s", fpath)
			log.Error(err)
			return &model.OssType{}
		}
		if result.StatusCode != 200 {
			return &model.OssType{}
		}
	}

	if isGzFile {
		metaRet, err := obsClient.SetObjectMetadata(&obs.SetObjectMetadataInput{
			Bucket:          bucketName,
			Key:             obsKey,
			ContentEncoding: "gzip",
			ContentType:     "text/plain",
		})
		fmt.Println(metaRet, err)

		if err != nil {
			metaRet, err = obsClient.SetObjectMetadata(&obs.SetObjectMetadataInput{
				Bucket:          bucketName,
				Key:             obsKey,
				ContentEncoding: "gzip",
				ContentType:     "text/plain",
			})
			fmt.Println(metaRet, err)
		}
	}

	fi, err := os.Stat(fpath)
	size := int64(1)
	if err == nil {
		size = fi.Size()
	}

	obsConf := conf.AppConfig.Obs

	return &model.OssType{Url: fmt.Sprintf("//%s.%s/%s", bucketName, obsConf.Endpoint, obsKey), Size: int64(size)}
}
