package stub

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

// GenerateAccountLocalStub generates a deterministic cp9-owned account service
// stub for local OpenAPI dogfood. It implements only the account and ACL calls
// needed by the Premium Reserved create path.
func GenerateAccountLocalStub(cfg *Config) (map[string]string, error) {
	httpPort := cfg.HTTPPort
	if httpPort == 0 {
		httpPort = DefaultHTTPPort
	}
	grpcPort := cfg.GRPCPort
	if grpcPort == 0 {
		grpcPort = DefaultGRPCPort
	}

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

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

type accountLocalTemplateData struct {
	Service  string
	HTTPPort int
	GRPCPort int
}

var accountLocalMainTmpl = template.Must(template.New("account-local-main").Parse(`package main

import (
	"errors"
	"fmt"
	"io"
	"log"
	"net"
	"net/http"
	"strings"

	"google.golang.org/grpc"
	"google.golang.org/grpc/codes"
	"google.golang.org/grpc/status"
	"google.golang.org/protobuf/proto"
	"google.golang.org/protobuf/reflect/protodesc"
	"google.golang.org/protobuf/reflect/protoreflect"
	"google.golang.org/protobuf/types/descriptorpb"
	"google.golang.org/protobuf/types/dynamicpb"
	"google.golang.org/protobuf/types/known/emptypb"
)

const (
	localOrgID       = uint64(100)
	localProjectID   = uint64(200)
	localUserID      = uint64(1000)
	localAPIKeyID    = uint64(9000)
	localPublicKey   = "cp9-local-public-key"
	localUserEmail   = "cp9-local@example.com"
	localProjectName = "cp9-local-premium-reserved"
	localClusterAuthJWKS = "{\"keys\":[{\"kty\":\"RSA\",\"n\":\"omdju8m0W9poISOC0FEIyWfNMCjM-0Lrs65TY2kzYvkjlY9qptIlOu9q4JOxGs-ZN_-HaPlhSI3iFFEqg9ANyx4ilRlOuR4SU5FodSD_TyeDHSiSAyGbNaHYCv-XHLsNbrruCdJ5bYUBBgvwaRiURanh5v0xng646oLB8TT7YZ2dBu19L313-LAg_Rs7hTnjFKYklyaMrtwolWA3Lq8dw4iBawbByUePszmpxr0XM3rBmhcl98Gx4_B3Zu02hH6NBqpDEtp5jrwROJZfArivyFZG0VpUZsnohFW84CET-EreLuj00ojKPFHAUXsWzSWT580RKeCSoUEOUg1LAqYYMQ\",\"e\":\"AQAB\",\"kid\":\"cp9-local\",\"use\":\"sig\",\"alg\":\"RS256\"}]}"
)

type descriptorRegistry map[string]protoreflect.MessageDescriptor

func main() {
	descs := mustAccountDescriptors()
	mux := http.NewServeMux()
	mux.HandleFunc("GET /healthz", func(w http.ResponseWriter, _ *http.Request) {
		w.WriteHeader(http.StatusOK)
		fmt.Fprint(w, "{\"status\":\"ok\"}")
	})
	mux.HandleFunc("/", func(w http.ResponseWriter, _ *http.Request) {
		w.Header().Set("Content-Type", "application/json")
		w.WriteHeader(http.StatusOK)
		fmt.Fprint(w, "{\"service\":\"{{.Service}}\",\"kind\":\"account-local\"}")
	})

	grpcAddr := fmt.Sprintf(":%d", {{.GRPCPort}})
	grpcServer := grpc.NewServer(grpc.UnknownServiceHandler(func(_ any, stream grpc.ServerStream) error {
		var req emptypb.Empty
		if err := stream.RecvMsg(&req); err != nil && err != io.EOF {
			return status.Errorf(codes.Internal, "decode: %v", err)
		}
		fullMethod, ok := grpc.MethodFromServerStream(stream)
		if !ok {
			return status.Error(codes.Internal, "missing grpc method")
		}
		resp, err := accountResponse(descs, fullMethod)
		if err != nil {
			if errors.Is(err, errUnknownMethod) {
				return status.Errorf(codes.Unimplemented, "%s", err)
			}
			return status.Errorf(codes.Internal, "%v", err)
		}
		return stream.SendMsg(resp)
	}))

	lis, err := net.Listen("tcp", grpcAddr)
	if err != nil {
		log.Fatalf("failed to listen on %s: %v", grpcAddr, err)
	}

	httpAddr := fmt.Sprintf(":%d", {{.HTTPPort}})
	errCh := make(chan error, 2)
	go func() { errCh <- http.ListenAndServe(httpAddr, mux) }()
	go func() { errCh <- grpcServer.Serve(lis) }()

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

var errUnknownMethod = errors.New("unknown account-local method")

func accountResponse(descs descriptorRegistry, fullMethod string) (proto.Message, error) {
	method := fullMethod[strings.LastIndex(fullMethod, "/")+1:]
	switch fullMethod {
	case "/account.AccountAPIService/GetOpenAPIAccess":
		return accountOpenAPIAccess(descs), nil
	case "/acl.AccessService/GetOpenAPIAccess":
		return aclOpenAPIAccess(descs), nil
	}
	switch method {
	case "GetApiKeyByAccessKey":
		return accountGetAPIKey(descs), nil
	case "GetUserByToken":
		return accountUserByToken(descs), nil
	case "ListProjectsByOrgs", "ListProjectsByOrgsInUser":
		return accountOrgProjects(descs, "account."+method+"Response"), nil
	case "ListProjects":
		return accountListProjects(descs), nil
	case "GetDBUsers":
		return accountDBUsers(descs), nil
	case "GetProjectByID":
		return accountProjectResponse(descs), nil
	case "GetOrgByID":
		return accountOrgResponse(descs), nil
	case "GetOrCreateSystemUser", "GetSystemUser", "GetUserByEmail", "GetUserByID":
		return accountUserResponse(descs, "account."+method+"Response"), nil
	case "GetAuthUser":
		return accountAuthUser(descs), nil
	case "ListIdentityRoleBindings":
		return accountIdentityRoleBindings(descs), nil
	case "HasPermissionForIdentity":
		return accountIdentityPermission(descs), nil
	case "HasPermissionForUser":
		return accountPermission(descs), nil
	case "CheckIdentityAndPermission":
		return accountCheckPermission(descs), nil
	case "AddClusterRelations", "DeleteClusterRelations":
		return accountBaseResponse(descs, "account."+method+"Rsp"), nil
	case "GetClusterAuthPubKey":
		return accountClusterAuthPubKey(descs), nil
	case "GetOpenAPIAccess":
		return accountOpenAPIAccess(descs), nil
	default:
		return nil, fmt.Errorf("%w: %s", errUnknownMethod, fullMethod)
	}
}

func accountGetAPIKey(descs descriptorRegistry) *dynamicpb.Message {
	m := newMsg(descs, "account.GetApiKeyByAccessKeyRsp")
	setMsg(m, "api_key", apiKey(descs))
	appendMsg(m, "resource_infos", accountResource(descs, "ORG", localOrgID))
	appendMsg(m, "resource_infos", accountResource(descs, "PROJECT", localProjectID))
	setMsg(m, "base_resp", newMsg(descs, "account.BaseResp"))
	return m
}

func accountUserByToken(descs descriptorRegistry) *dynamicpb.Message {
	m := newMsg(descs, "account.GetUserByTokenResponse")
	setMsg(m, "user", user(descs))
	setString(m, "token_type", "local")
	setMsg(m, "base_resp", newMsg(descs, "account.BaseResp"))
	return m
}

func accountAuthUser(descs descriptorRegistry) *dynamicpb.Message {
	m := newMsg(descs, "account.GetAuthUserResponse")
	setMsg(m, "user", user(descs))
	setMsg(m, "org", org(descs))
	appendMsg(m, "available_projects", project(descs))
	setBool(m, "is_impersonated", false)
	setInt32(m, "org_role", 1)
	setMsg(m, "base_resp", newMsg(descs, "account.BaseResp"))
	return m
}

func accountOrgProjects(descs descriptorRegistry, name string) *dynamicpb.Message {
	m := newMsg(descs, name)
	orgProject := newMsg(descs, "account.OrgProject")
	setUint64(orgProject, "org_id", localOrgID)
	appendMsg(orgProject, "projects", project(descs))
	appendMsg(m, "projects", orgProject)
	setMsg(m, "base_resp", newMsg(descs, "account.BaseResp"))
	return m
}

func accountListProjects(descs descriptorRegistry) *dynamicpb.Message {
	m := newMsg(descs, "account.ListProjectsResponse")
	appendMsg(m, "projects", project(descs))
	setMsg(m, "base_resp", newMsg(descs, "account.BaseResp"))
	return m
}

func accountDBUsers(descs descriptorRegistry) *dynamicpb.Message {
	m := newMsg(descs, "account.GetDBUsersResp")
	appendMsg(m, "db_users", dbUser(descs))
	setMsg(m, "base_resp", newMsg(descs, "account.BaseResp"))
	return m
}

func accountProjectResponse(descs descriptorRegistry) *dynamicpb.Message {
	m := newMsg(descs, "account.GetProjectByIDResponse")
	setMsg(m, "project", project(descs))
	setMsg(m, "base_resp", newMsg(descs, "account.BaseResp"))
	return m
}

func accountOrgResponse(descs descriptorRegistry) *dynamicpb.Message {
	m := newMsg(descs, "account.GetOrgByIDResponse")
	setMsg(m, "org", org(descs))
	setMsg(m, "base_resp", newMsg(descs, "account.BaseResp"))
	return m
}

func accountUserResponse(descs descriptorRegistry, name string) *dynamicpb.Message {
	m := newMsg(descs, name)
	setMsg(m, "user", user(descs))
	setMsg(m, "base_resp", newMsg(descs, "account.BaseResp"))
	return m
}

func accountIdentityPermission(descs descriptorRegistry) *dynamicpb.Message {
	m := newMsg(descs, "account.RbacIdentityPermissionResponse")
	setBool(m, "auth_result", true)
	setString(m, "auth_message", "ok")
	setMsg(m, "base_resp", newMsg(descs, "account.BaseResp"))
	return m
}

func accountIdentityRoleBindings(descs descriptorRegistry) *dynamicpb.Message {
	m := newMsg(descs, "account.ListIdentityRoleBindingsRsp")
	setMsg(m, "org_role", roleBinding(descs, "ORG", localOrgID, "org:owner", 1))
	appendMsg(m, "project_roles", roleBinding(descs, "PROJECT", localProjectID, "project:owner", 1))
	setMsg(m, "base_resp", newMsg(descs, "account.BaseResp"))
	return m
}

func accountPermission(descs descriptorRegistry) *dynamicpb.Message {
	m := newMsg(descs, "account.RbacPermissionResponse")
	setBool(m, "auth_result", true)
	setMsg(m, "base_resp", newMsg(descs, "account.BaseResp"))
	return m
}

func accountCheckPermission(descs descriptorRegistry) *dynamicpb.Message {
	m := newMsg(descs, "account.CheckIdentityAndPermissionResponse")
	setInt32(m, "check_ret", 0)
	setString(m, "message", "ok")
	setMsg(m, "base_resp", newMsg(descs, "account.BaseResp"))
	return m
}

func accountBaseResponse(descs descriptorRegistry, name string) *dynamicpb.Message {
	m := newMsg(descs, name)
	setMsg(m, "base_resp", newMsg(descs, "account.BaseResp"))
	return m
}

func accountClusterAuthPubKey(descs descriptorRegistry) *dynamicpb.Message {
	m := newMsg(descs, "account.GetClusterAuthPubKeyRsp")
	setString(m, "jwks", localClusterAuthJWKS)
	setMsg(m, "base_resp", newMsg(descs, "account.BaseResp"))
	return m
}

func accountOpenAPIAccess(descs descriptorRegistry) *dynamicpb.Message {
	m := newMsg(descs, "account.GetOpenAPIAccessResp")
	setMsg(m, "org", accountAccessOrg(descs))
	appendMsg(m, "projects", accountAccessProject(descs))
	return m
}

func aclOpenAPIAccess(descs descriptorRegistry) *dynamicpb.Message {
	m := newMsg(descs, "acl.GetOpenAPIAccessResp")
	setMsg(m, "org", aclAccessOrg(descs))
	appendMsg(m, "projects", aclAccessProject(descs))
	return m
}

func project(descs descriptorRegistry) *dynamicpb.Message {
	m := newMsg(descs, "account.Project")
	setUint64(m, "id", localProjectID)
	setUint64(m, "tenant_id", localOrgID)
	setString(m, "name", localProjectName)
	setString(m, "status", "active")
	setInt32(m, "type", 3)
	return m
}

func org(descs descriptorRegistry) *dynamicpb.Message {
	m := newMsg(descs, "account.Org")
	setUint64(m, "id", localOrgID)
	setString(m, "name", "cp9-local-org")
	setString(m, "time_zone", "UTC")
	setString(m, "status", "active")
	setString(m, "plan", "Premium")
	return m
}

func user(descs descriptorRegistry) *dynamicpb.Message {
	m := newMsg(descs, "account.User")
	setUint64(m, "id", localUserID)
	setString(m, "first_name", "cp9")
	setString(m, "last_name", "local")
	setString(m, "email", localUserEmail)
	setInt32(m, "status", 1)
	return m
}

func dbUser(descs descriptorRegistry) *dynamicpb.Message {
	user := newMsg(descs, "account.DBUser")
	setString(user, "username", "cp9_local_admin")
	setString(user, "email", localUserEmail)
	setEnum(user, "builtin_role", 1)
	setUint64(user, "org_id", localOrgID)
	setUint64(user, "project_id", localProjectID)
	return user
}

func apiKey(descs descriptorRegistry) *dynamicpb.Message {
	m := newMsg(descs, "account.ApiKey")
	setUint64(m, "id", localAPIKeyID)
	setString(m, "access_key", localPublicKey)
	setString(m, "created_by", localUserEmail)
	appendMsg(m, "resource_infos", accountResource(descs, "ORG", localOrgID))
	appendMsg(m, "resource_infos", accountResource(descs, "PROJECT", localProjectID))
	return m
}

func accountResource(descs descriptorRegistry, scope string, id uint64) *dynamicpb.Message {
	m := newMsg(descs, "account.ApiKeyResourceInfo")
	setString(m, "scope_type", scope)
	setUint64(m, "resource_id", id)
	setString(m, "role", "owner")
	setUint64(m, "role_id", 1)
	return m
}

func roleBinding(descs descriptorRegistry, scope string, id uint64, role string, roleID uint64) *dynamicpb.Message {
	m := newMsg(descs, "account.ScopeRoleBinding")
	setString(m, "scope_type", scope)
	setUint64(m, "scope_id", id)
	setString(m, "role", role)
	setUint64(m, "role_id", roleID)
	return m
}

func accountAccessOrg(descs descriptorRegistry) *dynamicpb.Message {
	m := newMsg(descs, "account.OrgAccessResp")
	setUint64(m, "id", localOrgID)
	appendString(m, "roles", "owner")
	return m
}

func accountAccessProject(descs descriptorRegistry) *dynamicpb.Message {
	m := newMsg(descs, "account.ProjectAccessResp")
	setUint64(m, "id", localProjectID)
	appendString(m, "roles", "owner")
	return m
}

func aclAccessOrg(descs descriptorRegistry) *dynamicpb.Message {
	m := newMsg(descs, "acl.OrgAccessResp")
	setUint64(m, "id", localOrgID)
	appendString(m, "roles", "owner")
	appendInt32(m, "org_roles", 1)
	return m
}

func aclAccessProject(descs descriptorRegistry) *dynamicpb.Message {
	m := newMsg(descs, "acl.ProjectAccessResp")
	setUint64(m, "id", localProjectID)
	appendString(m, "roles", "owner")
	appendInt32(m, "project_roles", 1)
	return m
}

func newMsg(descs descriptorRegistry, name string) *dynamicpb.Message {
	desc, ok := descs[name]
	if !ok {
		panic("missing message descriptor: " + name)
	}
	return dynamicpb.NewMessage(desc)
}

func setString(m *dynamicpb.Message, name string, value string) {
	m.Set(m.Descriptor().Fields().ByName(protoreflect.Name(name)), protoreflect.ValueOfString(value))
}

func appendString(m *dynamicpb.Message, name string, value string) {
	list := m.Mutable(m.Descriptor().Fields().ByName(protoreflect.Name(name))).List()
	list.Append(protoreflect.ValueOfString(value))
}

func setUint64(m *dynamicpb.Message, name string, value uint64) {
	m.Set(m.Descriptor().Fields().ByName(protoreflect.Name(name)), protoreflect.ValueOfUint64(value))
}

func setInt32(m *dynamicpb.Message, name string, value int32) {
	m.Set(m.Descriptor().Fields().ByName(protoreflect.Name(name)), protoreflect.ValueOfInt32(value))
}

func setEnum(m *dynamicpb.Message, name string, value int32) {
	m.Set(
		m.Descriptor().Fields().ByName(protoreflect.Name(name)),
		protoreflect.ValueOfEnum(protoreflect.EnumNumber(value)),
	)
}

func appendInt32(m *dynamicpb.Message, name string, value int32) {
	list := m.Mutable(m.Descriptor().Fields().ByName(protoreflect.Name(name))).List()
	list.Append(protoreflect.ValueOfInt32(value))
}

func setBool(m *dynamicpb.Message, name string, value bool) {
	m.Set(m.Descriptor().Fields().ByName(protoreflect.Name(name)), protoreflect.ValueOfBool(value))
}

func setMsg(m *dynamicpb.Message, name string, value *dynamicpb.Message) {
	m.Set(m.Descriptor().Fields().ByName(protoreflect.Name(name)), protoreflect.ValueOfMessage(value))
}

func appendMsg(m *dynamicpb.Message, name string, value *dynamicpb.Message) {
	list := m.Mutable(m.Descriptor().Fields().ByName(protoreflect.Name(name))).List()
	list.Append(protoreflect.ValueOfMessage(value))
}

func mustAccountDescriptors() descriptorRegistry {
	descs := descriptorRegistry{}
	for _, f := range []*descriptorpb.FileDescriptorProto{accountFile(), aclFile()} {
		fd, err := protodesc.NewFile(f, nil)
		if err != nil {
			log.Fatalf("build account-local descriptors: %v", err)
		}
		msgs := fd.Messages()
		for i := 0; i < msgs.Len(); i++ {
			msg := msgs.Get(i)
			descs[string(msg.FullName())] = msg
		}
	}
	return descs
}

func accountFile() *descriptorpb.FileDescriptorProto {
	return &descriptorpb.FileDescriptorProto{
		Syntax:  proto.String("proto3"),
		Name:    proto.String("account_local.proto"),
		Package: proto.String("account"),
		MessageType: []*descriptorpb.DescriptorProto{
			msg("BaseResp"),
			msg("User",
				scalar("id", 1, descriptorpb.FieldDescriptorProto_TYPE_UINT64),
				scalar("first_name", 7, descriptorpb.FieldDescriptorProto_TYPE_STRING),
				scalar("last_name", 8, descriptorpb.FieldDescriptorProto_TYPE_STRING),
				scalar("email", 9, descriptorpb.FieldDescriptorProto_TYPE_STRING),
				scalar("status", 10, descriptorpb.FieldDescriptorProto_TYPE_INT32),
			),
			msg("Project",
				scalar("id", 1, descriptorpb.FieldDescriptorProto_TYPE_UINT64),
				scalar("tenant_id", 4, descriptorpb.FieldDescriptorProto_TYPE_UINT64),
				scalar("name", 5, descriptorpb.FieldDescriptorProto_TYPE_STRING),
				scalar("status", 6, descriptorpb.FieldDescriptorProto_TYPE_STRING),
				scalar("type", 10, descriptorpb.FieldDescriptorProto_TYPE_INT32),
			),
			msg("Org",
				scalar("id", 1, descriptorpb.FieldDescriptorProto_TYPE_UINT64),
				scalar("name", 4, descriptorpb.FieldDescriptorProto_TYPE_STRING),
				scalar("time_zone", 8, descriptorpb.FieldDescriptorProto_TYPE_STRING),
				scalar("status", 9, descriptorpb.FieldDescriptorProto_TYPE_STRING),
				scalar("plan", 10, descriptorpb.FieldDescriptorProto_TYPE_STRING),
				scalar("third_party_account_provider", 17, descriptorpb.FieldDescriptorProto_TYPE_STRING),
			),
			msg("OrgProject",
				scalar("org_id", 1, descriptorpb.FieldDescriptorProto_TYPE_UINT64),
				repeatedMsg("projects", 2, ".account.Project"),
			),
			msg("ApiKeyResourceInfo",
				scalar("scope_type", 1, descriptorpb.FieldDescriptorProto_TYPE_STRING),
				scalar("resource_id", 2, descriptorpb.FieldDescriptorProto_TYPE_UINT64),
				scalar("role", 3, descriptorpb.FieldDescriptorProto_TYPE_STRING),
				scalar("role_id", 4, descriptorpb.FieldDescriptorProto_TYPE_UINT64),
			),
			msg("ApiKey",
				scalar("id", 1, descriptorpb.FieldDescriptorProto_TYPE_UINT64),
				scalar("access_key", 2, descriptorpb.FieldDescriptorProto_TYPE_STRING),
				scalar("created_by", 6, descriptorpb.FieldDescriptorProto_TYPE_STRING),
				repeatedMsg("resource_infos", 10, ".account.ApiKeyResourceInfo"),
			),
			msg("OrgAccessResp",
				scalar("id", 1, descriptorpb.FieldDescriptorProto_TYPE_UINT64),
				repeatedScalar("roles", 2, descriptorpb.FieldDescriptorProto_TYPE_STRING),
			),
			msg("ProjectAccessResp",
				scalar("id", 1, descriptorpb.FieldDescriptorProto_TYPE_UINT64),
				repeatedScalar("roles", 2, descriptorpb.FieldDescriptorProto_TYPE_STRING),
			),
			response("GetUserByEmailResponse", "user", ".account.User"),
			response("GetUserByIDResponse", "user", ".account.User"),
			msg("GetUserByTokenResponse",
				msgField("user", 1, ".account.User"),
				scalar("token_type", 2, descriptorpb.FieldDescriptorProto_TYPE_STRING),
				msgField("base_resp", 255, ".account.BaseResp"),
			),
			msg("GetAuthUserResponse",
				msgField("user", 1, ".account.User"),
				msgField("org", 2, ".account.Org"),
				repeatedMsg("available_projects", 3, ".account.Project"),
				scalar("is_impersonated", 4, descriptorpb.FieldDescriptorProto_TYPE_BOOL),
				scalar("org_role", 5, descriptorpb.FieldDescriptorProto_TYPE_INT32),
				msgField("base_resp", 255, ".account.BaseResp"),
			),
			response("GetSystemUserResponse", "user", ".account.User"),
			response("GetOrCreateSystemUserResponse", "user", ".account.User"),
			response("GetProjectByIDResponse", "project", ".account.Project"),
			msg("ListProjectsResponse",
				repeatedMsg("projects", 1, ".account.Project"),
				msgField("base_resp", 255, ".account.BaseResp"),
			),
			msg("DBUser",
				scalar("username", 1, descriptorpb.FieldDescriptorProto_TYPE_STRING),
				scalar("email", 2, descriptorpb.FieldDescriptorProto_TYPE_STRING),
				enumField("builtin_role", 3, ".account.BuiltinRole"),
				scalar("org_id", 4, descriptorpb.FieldDescriptorProto_TYPE_UINT64),
				scalar("project_id", 5, descriptorpb.FieldDescriptorProto_TYPE_UINT64),
				repeatedScalar("custom_roles", 6, descriptorpb.FieldDescriptorProto_TYPE_STRING),
			),
			msg("GetDBUsersResp",
				repeatedMsg("db_users", 1, ".account.DBUser"),
				msgField("base_resp", 255, ".account.BaseResp"),
			),
			msg("ListProjectsByOrgsResponse",
				repeatedMsg("projects", 1, ".account.OrgProject"),
				msgField("base_resp", 255, ".account.BaseResp"),
			),
			msg("ListProjectsByOrgsInUserResponse",
				repeatedMsg("projects", 1, ".account.OrgProject"),
				msgField("base_resp", 255, ".account.BaseResp"),
			),
			response("GetOrgByIDResponse", "org", ".account.Org"),
			msg("RbacIdentityPermissionResponse",
				scalar("auth_result", 1, descriptorpb.FieldDescriptorProto_TYPE_BOOL),
				scalar("auth_message", 2, descriptorpb.FieldDescriptorProto_TYPE_STRING),
				msgField("base_resp", 255, ".account.BaseResp"),
			),
			msg("ScopeRoleBinding",
				scalar("scope_type", 1, descriptorpb.FieldDescriptorProto_TYPE_STRING),
				scalar("scope_id", 2, descriptorpb.FieldDescriptorProto_TYPE_UINT64),
				scalar("role", 3, descriptorpb.FieldDescriptorProto_TYPE_STRING),
				scalar("role_id", 4, descriptorpb.FieldDescriptorProto_TYPE_UINT64),
			),
			msg("ListIdentityRoleBindingsRsp",
				msgField("org_role", 1, ".account.ScopeRoleBinding"),
				repeatedMsg("project_roles", 2, ".account.ScopeRoleBinding"),
				repeatedMsg("cluster_roles", 3, ".account.ScopeRoleBinding"),
				msgField("base_resp", 255, ".account.BaseResp"),
			),
			msg("RbacPermissionResponse",
				scalar("auth_result", 1, descriptorpb.FieldDescriptorProto_TYPE_BOOL),
				msgField("base_resp", 255, ".account.BaseResp"),
			),
			msg("CheckIdentityAndPermissionResponse",
				scalar("check_ret", 1, descriptorpb.FieldDescriptorProto_TYPE_INT32),
				scalar("message", 2, descriptorpb.FieldDescriptorProto_TYPE_STRING),
				msgField("base_resp", 255, ".account.BaseResp"),
			),
			msg("GetApiKeyByAccessKeyRsp",
				msgField("api_key", 1, ".account.ApiKey"),
				repeatedMsg("resource_infos", 2, ".account.ApiKeyResourceInfo"),
				msgField("base_resp", 255, ".account.BaseResp"),
			),
			msg("AddClusterRelationsRsp",
				msgField("base_resp", 255, ".account.BaseResp"),
			),
			msg("DeleteClusterRelationsRsp",
				msgField("base_resp", 255, ".account.BaseResp"),
			),
			msg("GetClusterAuthPubKeyRsp",
				scalar("jwks", 1, descriptorpb.FieldDescriptorProto_TYPE_STRING),
				msgField("base_resp", 255, ".account.BaseResp"),
			),
			msg("GetOpenAPIAccessResp",
				msgField("org", 1, ".account.OrgAccessResp"),
				repeatedMsg("projects", 2, ".account.ProjectAccessResp"),
			),
		},
		EnumType: []*descriptorpb.EnumDescriptorProto{
			enum("BuiltinRole",
				enumValue("BUILTIN_ROLE_UNSPECIFIED", 0),
				enumValue("BUILTIN_ROLE_USER_ADMIN", 1),
				enumValue("BUILTIN_ROLE_USER_MEMBER", 2),
				enumValue("BUILTIN_ROLE_USER_OBSERVER", 3),
			),
		},
	}
}

func aclFile() *descriptorpb.FileDescriptorProto {
	return &descriptorpb.FileDescriptorProto{
		Syntax:  proto.String("proto3"),
		Name:    proto.String("acl_local.proto"),
		Package: proto.String("acl"),
		MessageType: []*descriptorpb.DescriptorProto{
			msg("OrgAccessResp",
				scalar("id", 1, descriptorpb.FieldDescriptorProto_TYPE_UINT64),
				repeatedScalar("roles", 2, descriptorpb.FieldDescriptorProto_TYPE_STRING),
				repeatedScalar("org_roles", 3, descriptorpb.FieldDescriptorProto_TYPE_INT32),
			),
			msg("ProjectAccessResp",
				scalar("id", 1, descriptorpb.FieldDescriptorProto_TYPE_UINT64),
				repeatedScalar("roles", 2, descriptorpb.FieldDescriptorProto_TYPE_STRING),
				repeatedScalar("project_roles", 3, descriptorpb.FieldDescriptorProto_TYPE_INT32),
			),
			msg("GetOpenAPIAccessResp",
				msgField("org", 1, ".acl.OrgAccessResp"),
				repeatedMsg("projects", 2, ".acl.ProjectAccessResp"),
			),
		},
	}
}

func msg(name string, fields ...*descriptorpb.FieldDescriptorProto) *descriptorpb.DescriptorProto {
	return &descriptorpb.DescriptorProto{Name: proto.String(name), Field: fields}
}

func response(name, field, typ string) *descriptorpb.DescriptorProto {
	return msg(name,
		msgField(field, 1, typ),
		msgField("base_resp", 255, ".account.BaseResp"),
	)
}

func scalar(name string, number int32, typ descriptorpb.FieldDescriptorProto_Type) *descriptorpb.FieldDescriptorProto {
	return &descriptorpb.FieldDescriptorProto{
		Name:   proto.String(name),
		Number: proto.Int32(number),
		Label:  descriptorpb.FieldDescriptorProto_LABEL_OPTIONAL.Enum(),
		Type:   typ.Enum(),
	}
}

func repeatedScalar(name string, number int32, typ descriptorpb.FieldDescriptorProto_Type) *descriptorpb.FieldDescriptorProto {
	return &descriptorpb.FieldDescriptorProto{
		Name:   proto.String(name),
		Number: proto.Int32(number),
		Label:  descriptorpb.FieldDescriptorProto_LABEL_REPEATED.Enum(),
		Type:   typ.Enum(),
	}
}

func msgField(name string, number int32, typ string) *descriptorpb.FieldDescriptorProto {
	return &descriptorpb.FieldDescriptorProto{
		Name:     proto.String(name),
		Number:   proto.Int32(number),
		Label:    descriptorpb.FieldDescriptorProto_LABEL_OPTIONAL.Enum(),
		Type:     descriptorpb.FieldDescriptorProto_TYPE_MESSAGE.Enum(),
		TypeName: proto.String(typ),
	}
}

func enumField(name string, number int32, typ string) *descriptorpb.FieldDescriptorProto {
	return &descriptorpb.FieldDescriptorProto{
		Name:     proto.String(name),
		Number:   proto.Int32(number),
		Label:    descriptorpb.FieldDescriptorProto_LABEL_OPTIONAL.Enum(),
		Type:     descriptorpb.FieldDescriptorProto_TYPE_ENUM.Enum(),
		TypeName: proto.String(typ),
	}
}

func repeatedMsg(name string, number int32, typ string) *descriptorpb.FieldDescriptorProto {
	return &descriptorpb.FieldDescriptorProto{
		Name:     proto.String(name),
		Number:   proto.Int32(number),
		Label:    descriptorpb.FieldDescriptorProto_LABEL_REPEATED.Enum(),
		Type:     descriptorpb.FieldDescriptorProto_TYPE_MESSAGE.Enum(),
		TypeName: proto.String(typ),
	}
}

func enum(name string, values ...*descriptorpb.EnumValueDescriptorProto) *descriptorpb.EnumDescriptorProto {
	return &descriptorpb.EnumDescriptorProto{Name: proto.String(name), Value: values}
}

func enumValue(name string, number int32) *descriptorpb.EnumValueDescriptorProto {
	return &descriptorpb.EnumValueDescriptorProto{Name: proto.String(name), Number: proto.Int32(number)}
}
`))
