package openapi

import (
	"bytes"
	"context"
	"fmt"
	"io/ioutil"
	"strings"

	"github.com/google/uuid"
	"github.com/grpc-ecosystem/grpc-gateway/v2/runtime"
	"github.com/tidbcloud/account/commons/config"
	openapiv1veta1 "github.com/tidbcloud/account/idl/pbgen/proto/v1beta1"
	"github.com/tidbcloud/account/thirdparty/cdn"
	"github.com/tidbcloud/tiinf/errs"
	"github.com/tidbcloud/tiinf/monitor/tilog"
	"go.uber.org/zap"
	"google.golang.org/grpc"
	"google.golang.org/grpc/codes"
	"google.golang.org/grpc/status"
	"google.golang.org/protobuf/proto"

	"io"
	"net/http"
	"path/filepath"
)

const (
	contentTypeHeader = "content-type"
	headerJoinSep     = ","
)

type FileUploadService struct {
	openapiv1veta1.UnimplementedFileUploadServiceServer
}

func (s *FileUploadService) UploadLogo(ctx context.Context, req *openapiv1veta1.UploadLogoRequest) (*openapiv1veta1.UploadLogoResponse, error) {
	tilog.Info(ctx, "UploadLogo request", zap.Any("req", req))
	if req == nil || req.FileName == "" || len(req.Content) == 0 {
		tilog.Warn(ctx, "UploadLogo req invalid", zap.Any("req", req))
		return nil, errs.NewBadRequest("param invalid", nil)
	}
	contentTypes := strings.Split(req.Headers[contentTypeHeader], headerJoinSep)
	if len(contentTypes) == 0 {
		tilog.Warn(ctx, "UploadLogo not found content type", zap.Any("req", req))
		return nil, errs.NewBadRequest("param invalid", nil)
	}

	logoName, logoUrl, err := UploadFile2CDN(ctx, req.FileName, contentTypes[0], req.Content)
	if err != nil {
		tilog.Warn(ctx, "UploadLogo UploadFile2CDN error", zap.Any("req", req), zap.Any("err", err))
		return nil, errs.NewInternalUnknown(err.Error(), err)
	}
	rsp := &openapiv1veta1.UploadLogoResponse{
		LogoName: logoName,
		LogoUrl:  logoUrl,
	}
	tilog.Info(ctx, "UploadLogo response", zap.Any("rsp", rsp))
	return rsp, nil
}

func UploadFile2CDN(ctx context.Context, filename, contentType string, content []byte) (logoName, logoUrl string, err error) {
	extName := filepath.Ext(filename)
	newFileName := fmt.Sprintf("%s%s", uuid.NewString(), extName)

	size := int64(len(content))
	sectionReader := io.NewSectionReader(bytes.NewReader(content), 0, size)

	//bucket := "dbaas-ui-cloudfront-dev"
	bucket := config.Global().MspLogoConfig.MspCloudfrontS3bucket

	path := fmt.Sprintf("%s/%s", "msp", newFileName)

	err = cdn.UploadLogoImagWithReadSeeker(ctx, contentType, path, size, sectionReader, bucket)
	if err != nil {
		return "", "", err
	}

	logoName = newFileName
	logoUrl = fmt.Sprintf("%s/%s", config.Global().MspLogoConfig.MspCloudfrontDomain, path)

	return logoName, logoUrl, nil
}

func RegisterFileUploadServiceHandler(ctx context.Context, mux *runtime.ServeMux, conn *grpc.ClientConn) error {
	return RegisterFileUploadServiceHandlerClient(ctx, mux, openapiv1veta1.NewFileUploadServiceClient(conn))
}

func RegisterFileUploadServiceHandlerClient(ctx context.Context, mux *runtime.ServeMux, client openapiv1veta1.FileUploadServiceClient) error {

	mux.Handle("POST", pattern_FileUploadService_UploadLogo_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
		ctx, cancel := context.WithCancel(req.Context())
		defer cancel()
		inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
		var err error
		ctx, err = runtime.AnnotateContext(ctx, mux, req, "/openapi.v1beta1.FileUploadService/UploadLogo", runtime.WithHTTPPathPattern("/v1beta1/partners/logo:upload"))
		if err != nil {
			runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
			return
		}
		resp, md, err := request_FileUploadService_UploadLogo_0(ctx, inboundMarshaler, client, req, pathParams)
		ctx = runtime.NewServerMetadataContext(ctx, md)
		if err != nil {
			runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
			return
		}

		forward_FileUploadService_UploadLogo_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
	})

	return nil
}

func request_FileUploadService_UploadLogo_0(ctx context.Context, marshaler runtime.Marshaler, client openapiv1veta1.FileUploadServiceClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
	var protoReq openapiv1veta1.UploadLogoRequest
	var metadata runtime.ServerMetadata

	fileName, headers, content, err := parseFileMetadata(req)
	if err != nil {
		return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
	}
	protoReq.FileName = fileName
	protoReq.Headers = headers
	protoReq.Content = content

	msg, err := client.UploadLogo(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD))
	return msg, metadata, err

}

func parseFileMetadata(r *http.Request) (string, map[string]string, []byte, error) {
	if err := r.ParseMultipartForm(10 << 20); err != nil {
		return "", nil, nil, err
	}

	file, handler, err := r.FormFile("file")
	if err != nil {
		return "", nil, nil, err
	}
	defer func() {
		_ = file.Close()
	}()

	tilog.Info(r.Context(), "parseFileMetadata", zap.Any("fileName", handler.Filename), zap.Any("headers", handler.Header))

	headers := map[string]string{}
	for k, v := range handler.Header {
		jv := strings.Join(v, headerJoinSep)
		headers[strings.ToLower(k)] = jv
	}

	// 读取文件内容
	data, err := ioutil.ReadAll(file)
	if err != nil {
		return "", nil, nil, err
	}

	return handler.Filename, headers, data, nil
}

var (
	pattern_FileUploadService_UploadLogo_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2}, []string{"v1beta1", "partners", "logo"}, "upload"))
	forward_FileUploadService_UploadLogo_0 = runtime.ForwardResponseMessage
)
