package featureflags

import (
	"context"
	"fmt"
	"strconv"

	"code.justin.tv/eventbus/controlplane/internal/db"
	"code.justin.tv/eventbus/controlplane/internal/ldap"
	"code.justin.tv/eventbus/controlplane/rpc"

	"github.com/pkg/errors"
	"github.com/twitchtv/twirp"
)

type FeatureFlagsService struct {
	DB db.DB
}

func (s *FeatureFlagsService) IsEnabled(ctx context.Context, req *rpc.IsEnabledReq) (*rpc.IsEnabledResp, error) {
	ff, err := s.DB.FeatureFlagByName(ctx, req.Name)
	if err == db.ErrResourceNotFound {
		return nil, twirp.NewError(twirp.InvalidArgument, "feature flag  "+req.Name+" does not exist")
	} else if err != nil {
		return nil, errors.Wrap(err, "could not get feature flag")
	}

	return &rpc.IsEnabledResp{
		IsEnabled: ff.Enabled,
	}, nil
}

func (s *FeatureFlagsService) Enable(ctx context.Context, req *rpc.EnableReq) (*rpc.EnableResp, error) {
	err := s.DB.FeatureFlagEnable(ctx, req.Name)
	if err != nil {
		return nil, errors.Wrap(err, "could not enable feature flag")
	}
	return &rpc.EnableResp{}, nil
}

func (s *FeatureFlagsService) Disable(ctx context.Context, req *rpc.DisableReq) (*rpc.DisableResp, error) {
	err := s.DB.FeatureFlagDisable(ctx, req.Name)
	if err != nil {
		return nil, errors.Wrap(err, "could not disable feature flag")
	}
	return &rpc.DisableResp{}, nil
}

func (s *FeatureFlagsService) List(ctx context.Context, req *rpc.ListFeatureFlagsReq) (*rpc.ListFeatureFlagsResp, error) {
	ffs, err := s.DB.FeatureFlags(ctx)
	if err != nil {
		return nil, errors.Wrap(err, "could not get feature flags")
	}

	ffProtos := make([]*rpc.FeatureFlag, 0)
	for _, ff := range ffs {
		ffProtos = append(ffProtos, &rpc.FeatureFlag{
			Name:      ff.Name,
			IsEnabled: ff.Enabled,
		})
	}

	return &rpc.ListFeatureFlagsResp{
		FeatureFlags: ffProtos,
	}, nil
}

func (s *FeatureFlagsService) Create(ctx context.Context, req *rpc.CreateFeatureFlagsReq) (*rpc.CreateFeatureFlagsResp, error) {
	if !ldap.BelongsToGroup(ctx, ldap.EventManagersLDAPGroup) {
		return nil, twirp.NewError(twirp.PermissionDenied, fmt.Sprintf("access denied: must belong to group %s", ldap.EventManagersLDAPGroup))
	}

	if req.FeatureFlag == nil {
		return nil, twirp.NewError(twirp.InvalidArgument, "no feature flag provided")
	} else if req.FeatureFlag.Name == "" {
		return nil, twirp.NewError(twirp.InvalidArgument, "feature flag must have a valid name")
	}

	id, err := s.DB.FeatureFlagCreate(ctx, &db.FeatureFlag{
		Name:    req.FeatureFlag.Name,
		Enabled: req.FeatureFlag.IsEnabled,
	})
	if err != nil {
		return nil, errors.Wrap(err, "could not create feature flag")
	}

	return &rpc.CreateFeatureFlagsResp{
		Id: strconv.Itoa(id),
	}, nil
}

func (s *FeatureFlagsService) Delete(ctx context.Context, req *rpc.DeleteFeatureFlagsReq) (*rpc.DeleteFeatureFlagsResp, error) {
	if !ldap.BelongsToGroup(ctx, ldap.EventManagersLDAPGroup) {
		return nil, twirp.NewError(twirp.PermissionDenied, fmt.Sprintf("access denied: must belong to group %s", ldap.EventManagersLDAPGroup))
	}

	if req.Name == "" {
		return nil, twirp.NewError(twirp.InvalidArgument, "no feature flag name provided")
	}

	err := s.DB.FeatureFlagDelete(ctx, req.Name)
	if err != nil {
		return nil, errors.Wrap(err, "could not delete feature flag")
	}

	return &rpc.DeleteFeatureFlagsResp{}, nil
}
