/*
 * Copyright 2021 CloudWeGo Authors
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

// Package transmeta metadata handler for translation
package transmeta

import (
	"context"

	"github.com/cloudwego/kitex/pkg/remote"
	"github.com/cloudwego/kitex/pkg/remote/trans/nphttp2/metadata"
	"github.com/cloudwego/kitex/pkg/remote/transmeta"
	"github.com/cloudwego/kitex/pkg/rpcinfo"
	"github.com/cloudwego/kitex/transport"
)

// ClientHTTP2Handler default global client metadata handler
var ClientHTTP2Handler = &clientHTTP2Handler{}

type clientHTTP2Handler struct{}

var (
	_ remote.MetaHandler          = ClientHTTP2Handler
	_ remote.StreamingMetaHandler = ClientHTTP2Handler
)

// same as md.Append without strings.ToLower coz k must always be lower cases
func metaAppend(m metadata.MD, k, v string) {
	m[k] = append(m[k], v)
}

func (*clientHTTP2Handler) OnConnectStream(ctx context.Context) (context.Context, error) {
	ri := rpcinfo.GetRPCInfo(ctx)
	if !isGRPC(ri) {
		return ctx, nil
	}
	// append more meta into current metadata
	md, ok := metadata.FromOutgoingContext(ctx)
	if !ok {
		md = metadata.MD{}
	}
	metaAppend(md, transmeta.HTTPDestService, ri.To().ServiceName())
	metaAppend(md, transmeta.HTTPDestMethod, ri.To().Method())
	metaAppend(md, transmeta.HTTPSourceService, ri.From().ServiceName())
	metaAppend(md, transmeta.HTTPSourceMethod, ri.From().Method())
	return metadata.NewOutgoingContext(ctx, md), nil
}

func (*clientHTTP2Handler) OnReadStream(ctx context.Context) (context.Context, error) {
	return ctx, nil
}

func (ch *clientHTTP2Handler) WriteMeta(ctx context.Context, msg remote.Message) (context.Context, error) {
	return ctx, nil
}

func (ch *clientHTTP2Handler) ReadMeta(ctx context.Context, msg remote.Message) (context.Context, error) {
	return ctx, nil
}

// ServerHTTP2Handler default global server metadata handler
var ServerHTTP2Handler = &serverHTTP2Handler{}

type serverHTTP2Handler struct{}

func (*serverHTTP2Handler) OnConnectStream(ctx context.Context) (context.Context, error) {
	return ctx, nil
}

func (*serverHTTP2Handler) OnReadStream(ctx context.Context) (context.Context, error) {
	ri := rpcinfo.GetRPCInfo(ctx)
	if !isGRPC(ri) {
		return ctx, nil
	}
	md, ok := metadata.FromIncomingContext(ctx)
	if !ok {
		return ctx, nil
	}
	ci := rpcinfo.AsMutableEndpointInfo(ri.From())
	if ci != nil {
		if v := md[transmeta.HTTPSourceService]; len(v) != 0 {
			ci.SetServiceName(v[0])
		}
		if v := md[transmeta.HTTPSourceMethod]; len(v) != 0 {
			ci.SetMethod(v[0])
		}
	}
	return ctx, nil
}

func (sh *serverHTTP2Handler) WriteMeta(ctx context.Context, msg remote.Message) (context.Context, error) {
	return ctx, nil
}

func (sh *serverHTTP2Handler) ReadMeta(ctx context.Context, msg remote.Message) (context.Context, error) {
	return ctx, nil
}

func isGRPC(ri rpcinfo.RPCInfo) bool {
	return ri.Config().TransportProtocol()&transport.GRPC == transport.GRPC
}
