package stub

import (
	"fmt"
	"strings"
	"text/template"
)

// GenerateDataplaneManagerLocalStub generates a local gRPC stub for the
// dataplane-manager calls that cluster-ng-server makes during dogfood startup.
func GenerateDataplaneManagerLocalStub(cfg *Config) (map[string]string, error) {
	httpPort := cfg.HTTPPort
	if httpPort == 0 {
		httpPort = DefaultHTTPPort
	}
	grpcPort := cfg.GRPCPort
	if grpcPort == 0 {
		grpcPort = DefaultGRPCPort
	}

	data := dataplaneManagerTemplateData{
		Service:  cfg.Service,
		HTTPPort: httpPort,
		GRPCPort: grpcPort,
	}

	var b strings.Builder
	if err := dataplaneManagerMainTmpl.Execute(&b, data); err != nil {
		return nil, fmt.Errorf("execute dataplane-manager-local template: %w", err)
	}
	return map[string]string{"main.go": b.String()}, nil
}

type dataplaneManagerTemplateData struct {
	Service  string
	HTTPPort int
	GRPCPort int
}

var dataplaneManagerMainTmpl = template.Must(template.New("dataplane-manager-local-main").Parse(`package main

import (
	"encoding/json"
	"fmt"
	"io"
	"log"
	"net"
	"os"
	"path/filepath"
	"strings"
	"time"

	"google.golang.org/grpc"
	"google.golang.org/grpc/codes"
	"google.golang.org/grpc/status"
	"google.golang.org/protobuf/types/known/emptypb"
)

type boundaryEvent struct {
	Timestamp time.Time ` + "`json:\"timestamp\"`" + `
	Method    string    ` + "`json:\"method\"`" + `
}

func main() {
	recordFile := strings.TrimSpace(os.Getenv("CP9_RECORD_FILE"))
	if recordFile == "" {
		log.Fatal("CP9_RECORD_FILE is required")
	}

	httpAddr := fmt.Sprintf(":%d", {{.HTTPPort}})
	grpcAddr := fmt.Sprintf(":%d", {{.GRPCPort}})

	errCh := make(chan error, 2)
	go serveGRPC(httpAddr, recordFile, errCh)
	go serveGRPC(grpcAddr, recordFile, errCh)

	log.Printf("{{.Service}} dataplane-manager-local stub listening on grpc=%s and grpc=%s", httpAddr, grpcAddr)
	if err := <-errCh; err != nil {
		log.Fatalf("failed to serve: %v", err)
	}
}

func serveGRPC(addr, recordFile string, errCh chan<- error) {
	lis, err := net.Listen("tcp", addr)
	if err != nil {
		errCh <- fmt.Errorf("listen %s: %w", addr, err)
		return
	}

	server := grpc.NewServer(grpc.UnknownServiceHandler(func(_ any, stream grpc.ServerStream) error {
		method := "unknown"
		if streamMethod, ok := grpc.MethodFromServerStream(stream); ok {
			method = streamMethod
		}
		var req emptypb.Empty
		if err := stream.RecvMsg(&req); err != nil && err != io.EOF {
			return status.Errorf(codes.Internal, "decode: %v", err)
		}
		if err := appendJSONL(recordFile, boundaryEvent{
			Timestamp: time.Now().UTC(),
			Method:    method,
		}); err != nil {
			return status.Errorf(codes.Internal, "record boundary event: %v", err)
		}
		return stream.SendMsg(&emptypb.Empty{})
	}))
	errCh <- server.Serve(lis)
}

func appendJSONL(filePath string, value any) error {
	if err := os.MkdirAll(filepath.Dir(filePath), 0o755); err != nil {
		return fmt.Errorf("mkdir %s: %w", filepath.Dir(filePath), err)
	}
	f, err := os.OpenFile(filePath, os.O_CREATE|os.O_APPEND|os.O_WRONLY, 0o644)
	if err != nil {
		return fmt.Errorf("open %s: %w", filePath, err)
	}
	defer f.Close()

	data, err := json.Marshal(value)
	if err != nil {
		return fmt.Errorf("marshal %s: %w", filePath, err)
	}
	if _, err := f.Write(append(data, '\n')); err != nil {
		return fmt.Errorf("write %s: %w", filePath, err)
	}
	if err := f.Chmod(0o644); err != nil {
		return fmt.Errorf("chmod %s: %w", filePath, err)
	}
	return nil
}
`))
