package api

import (
	"baishuihu/conf"
	"errors"
	"fmt"
	"io"
	"math/rand"
	"os"
	"path"
	"strings"
	"time"
	"unsafe"

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

const (
	letterIdBits = 6
	letterIdMask = 1<<letterIdBits - 1
	letterIdMax  = 63 / letterIdBits
	letters      = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
)

var src = rand.NewSource(time.Now().UnixNano())

func Upload(r *GinRouter) {
	r.POST("upload/image", UploadImage)
	r.POST("upload/file", UploadFile)
	r.POST("upload/policy", UploadPolicy)
}

func UploadPolicy(c *gin.Context, apictx *ApiSession) (interface{}, error) {
	return ServiceObsUploadPolicy(c, apictx)
}

// 管理后台上传文件
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":     "obs",
	}
	return out, nil
}

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

// 本地上传图片
func UploadFile(c *gin.Context, apictx *ApiSession) (interface{}, error) {
	handle, err := c.FormFile("file")
	// 上传图像接口
	if err != nil {
		return nil, err
	}
	uploadFile, err := handle.Open()
	if err != nil {
		return nil, err
	}
	if handle.Size > 500<<20 {
		return nil, errors.New("最大支持单文件上传大小为50M")
	}

	saveFile, err := os.OpenFile(fmt.Sprintf("files/%s", handle.Filename), os.O_WRONLY|os.O_CREATE, 0666)
	if err != nil {
		return nil, err
	}
	io.Copy(saveFile, uploadFile)
	defer uploadFile.Close()
	defer saveFile.Close()

	// http://127.0.0.1:8101/public/
	out := map[string]string{
		"url":  fmt.Sprintf("%s/files/%s", conf.AppConfig.PublicBaseUrl, handle.Filename),
		"path": fmt.Sprintf("files/%s", handle.Filename),
	}
	return out, nil
}

// 本地上传图片
func UploadImage(c *gin.Context, apictx *ApiSession) (interface{}, error) {
	handle, err := c.FormFile("image")
	// 上传图像接口
	if err != nil {
		return nil, err
	}
	uploadFile, err := handle.Open()
	if err != nil {
		return nil, err
	}

	ext1 := strings.ToLower(path.Ext(handle.Filename))
	ext := strings.ToLower(ext1)
	limits := []string{".jpg", ".png", ".jpeg"}
	isAllow := false
	for _, limit := range limits {
		if ext == limit {
			isAllow = true
			break
		}
	}
	if !isAllow {
		return nil, errors.New("只支持jpg/jpeg/png图片上传")
	}

	// 保存图片
	os.Mkdir("images/", 0777)
	pathFileName := fmt.Sprintf("%s_%s%s", randName(8), time.Now().Format("20060102"), ext)
	saveFile, err := os.OpenFile(fmt.Sprintf("images/%s", pathFileName), os.O_WRONLY|os.O_CREATE, 0666)
	if err != nil {
		return nil, err
	}
	io.Copy(saveFile, uploadFile)
	defer uploadFile.Close()
	defer saveFile.Close()

	// http://127.0.0.1:8101/public/
	out := map[string]string{
		"url":  fmt.Sprintf("%s/images/%s", conf.AppConfig.PublicBaseUrl, pathFileName),
		"path": fmt.Sprintf("images/%s", pathFileName),
	}
	return out, nil
}

func randName(n int) string {
	b := make([]byte, n)
	for i, cache, remain := n-1, src.Int63(), letterIdMax; i >= 0; {
		if remain == 0 {
			cache, remain = src.Int63(), letterIdMax
		}
		if idx := int(cache & letterIdMask); idx < len(letters) {
			b[i] = letters[idx]
			i--
		}
		cache >>= letterIdBits
		remain--
	}
	return *(*string)(unsafe.Pointer(&b))
}
