package twirpmetric

import (
	"context"
	"path"

	"code.justin.tv/video/metrics-middleware/v2/operation"
	"github.com/twitchtv/twirp"
)

type Server struct {
	Starter *operation.Starter
}

func (s *Server) ServerHooks() *twirp.ServerHooks {
	opKey := new(int)

	return &twirp.ServerHooks{
		RequestRouted: func(ctx context.Context) (context.Context, error) {
			op := getOp(ctx)
			op.Kind = operation.KindServer

			ctx, span := s.Starter.StartOp(ctx, op)
			ctx = context.WithValue(ctx, opKey, span)
			return ctx, nil
		},
		Error: func(ctx context.Context, err twirp.Error) context.Context {
			if span, _ := ctx.Value(opKey).(*operation.Op); span != nil {
				span.SetStatus(statusFromTwirp(err.Code()))
			}
			return ctx
		},
		ResponseSent: func(ctx context.Context) {
			if span, _ := ctx.Value(opKey).(*operation.Op); span != nil {
				span.End()
			}
		},
	}
}

func getOp(ctx context.Context) operation.Name {
	var name operation.Name

	name.Method, _ = twirp.MethodName(ctx)
	name.Group, _ = twirp.ServiceName(ctx)
	if pkg, ok := twirp.PackageName(ctx); ok {
		name.Group = path.Join(pkg, name.Group)
	}

	return name
}

var status = map[string]int32{
	"":                    0,
	"canceled":            1,
	"unknown":             2,
	"invalid_argument":    3,
	"deadline_exceeded":   4,
	"not_found":           5,
	"already_exists":      6,
	"permission_denied":   7,
	"resource_exhausted":  8,
	"failed_precondition": 9,
	"aborted":             10,
	"out_of_range":        11,
	"unimplemented":       12,
	"internal":            13,
	"unavailable":         14,
	"data_loss":           15,
	"unauthenticated":     16,

	"bad_route": 12, // 12 = "unimplemented"
}

func statusFromTwirp(code twirp.ErrorCode) operation.Status {
	n, ok := status[string(code)]
	if !ok {
		n = 2 // "unknown"
	}
	return operation.Status{Code: n, Message: string(code)}
}
