package template

import (
	"context"

	"fmt"

	. "github.com/dave/jennifer/jen"
	"github.com/devimteam/microgen/generator/write_strategy"
)

const (
	EndpointsSetName = "EndpointsSet"
)

type endpointsTemplate struct {
	info *GenerationInfo
}

func NewEndpointsTemplate(info *GenerationInfo) Template {
	return &endpointsTemplate{
		info: info,
	}
}

func endpointsStructFieldName(str string) string {
	return str + "Endpoint"
}

// Renders endpoints file.
//
//		// This file was automatically generated by "microgen" utility.
//		// DO NOT EDIT.
//		package stringsvc
//
//		import (
//			context "context"
//			endpoint "github.com/go-kit/kit/endpoint"
//		)
//
//		type Endpoints struct {
//			CountEndpoint endpoint.Endpoint
//		}
//
func (t *endpointsTemplate) Render(ctx context.Context) write_strategy.Renderer {
	f := NewFile("transport")
	f.HeaderComment(t.info.FileHeader)

	f.Comment(fmt.Sprintf("%s implements %s API and used for transport purposes.", EndpointsSetName, t.info.Iface.Name))
	f.Type().Id(EndpointsSetName).StructFunc(func(g *Group) {
		for _, signature := range t.info.Iface.Methods {
			if t.info.AllowedMethods[signature.Name] {
				g.Id(endpointsStructFieldName(signature.Name)).Qual(PackagePathGoKitEndpoint, "Endpoint")
			}
		}
	}).Line()
	f.Add(t.serverMetrics(ctx)).Line()
	return f
}

func (endpointsTemplate) DefaultPath() string {
	return filenameBuilder(PathTransport, "endpoints")
}

func (t *endpointsTemplate) Prepare(ctx context.Context) error {
	return nil
}

func (t *endpointsTemplate) ChooseStrategy(ctx context.Context) (write_strategy.Strategy, error) {
	return write_strategy.NewCreateFileStrategy(t.info.OutputFilePath, t.DefaultPath()), nil
}

func (t *endpointsTemplate) serverMetrics(ctx context.Context) *Statement {
	s := &Statement{}
	if !Tags(ctx).Has(MetricsMiddlewareTag) {
		return s
	}
	const _name_ = "methodName"
	s.Func().Id("InstrumentingEndpoints").Call(Id("endpoints").Id(EndpointsSetName), Id("tracer").Qual(PackagePathOpenTracingGo, "Tracer")).Id(EndpointsSetName).BlockFunc(func(g *Group) {
		g.Return(Id(EndpointsSetName).Values(DictFunc(func(d Dict) {
			for _, signature := range t.info.Iface.Methods {
				if t.info.AllowedMethods[signature.Name] {
					d[Id(endpointsStructFieldName(signature.Name))] = Qual(PackagePathGoKitTracing, "TraceServer").Call(Id("tracer"), Lit(signature.Name)).Call(Id("endpoints").Dot(endpointsStructFieldName(signature.Name)))
				}
			}
		})))
	})
	s.Line()
	s.Line().Func().Id("LatencyMiddleware").Params(Id("dur").Qual(PackagePathGoKitMetrics, "Histogram"), Id(_name_).String()).Qual(PackagePathGoKitEndpoint, "Middleware").Block(
		Return().Func().Params(Id("next").Qual(PackagePathGoKitEndpoint, "Endpoint")).Qual(PackagePathGoKitEndpoint, "Endpoint").Block(
			Id("dur").Op(":=").Id("dur").Dot("With").Call(Lit("method"), Id(_name_)),
			Return().Func().Params(ctx_contextContext, Id("request").Interface()).Params(Id("response").Interface(), Err().Error()).Block(
				Defer().Func().Params(Id("begin").Qual(PackagePathTime, "Time")).Block(
					Id("dur").Dot("With").Call(Lit("success"), Qual(PackagePathFmt, "Sprint").Call(Err().Op("==").Nil())).
						Dot("Observe").Call(Qual(PackagePathTime, "Since").Call(Id("begin")).Dot("Seconds").Call()),
				).Call(Qual(PackagePathTime, "Now").Call()),
				Return().Id("next").Call(Id(_ctx_), Id("request")),
			),
		),
	)
	s.Line()
	s.Line().Func().Id("RequestFrequencyMiddleware").Params(Id("freq").Qual(PackagePathGoKitMetrics, "Gauge"), Id(_name_).String()).Qual(PackagePathGoKitEndpoint, "Middleware").Block(
		Return().Func().Params(Id("next").Qual(PackagePathGoKitEndpoint, "Endpoint")).Qual(PackagePathGoKitEndpoint, "Endpoint").Block(
			Id("freq").Op(":=").Id("freq").Dot("With").Call(Lit("method"), Id(_name_)),
			Return().Func().Params(ctx_contextContext, Id("request").Interface()).Params(Interface(), Error()).Block(
				Id("freq").Dot("Add").Call(Lit(1)),
				List(Id("response"), Err()).Op(":=").Id("next").Call(Id(_ctx_), Id("request")),
				Id("freq").Dot("Add").Call(Lit(-1)),
				Return(Id("response"), Err()),
			),
		),
	)
	return s
}
