package authfield

import (
	"context"
	"fmt"
	"log"
	"strings"

	"code.justin.tv/eventbus/admin-cli/internal/client"
	"code.justin.tv/eventbus/admin-cli/internal/environment"
	"code.justin.tv/eventbus/admin-cli/internal/output"
	"code.justin.tv/eventbus/admin-cli/internal/prompt"
	"code.justin.tv/eventbus/controlplane/infrastructure/rpc"
	"github.com/spf13/cobra"
)

var (
	grantType, iamRole, env, eventType, msg, field string
)

func init() {
	authGrantDeleteCommand.Flags().StringVarP(&grantType, "grant-type", "g", "", "type of grant to revoke, one of {publisher,subscriber,p,s}")
	authGrantDeleteCommand.MarkFlagRequired("grant-type")
	authGrantDeleteCommand.Flags().StringVarP(&iamRole, "iam-role", "i", "", "iam-role that has is having grant revoked")
	authGrantDeleteCommand.MarkFlagRequired("iam-role")
	authGrantDeleteCommand.Flags().StringVarP(&env, "environment", "e", "staging", "environment for which to revoke grant")
	authGrantDeleteCommand.MarkFlagRequired("environment")
	authGrantDeleteCommand.Flags().StringVarP(&eventType, "event-type", "t", "", "event type for which grant is revoked for")
	authGrantDeleteCommand.MarkFlagRequired("event-type")
	authGrantDeleteCommand.Flags().StringVarP(&msg, "message", "m", "", "message of auth field for sub grant revocation")
	authGrantDeleteCommand.Flags().StringVarP(&field, "field", "f", "", "field of auth field for sub grant revocation")
}

var authGrantDeleteCommand = &cobra.Command{
	Use:   "delete-grant",
	Short: "Revokes and then deletes an authorized grant for either a subscriber or publisher",
	Run: func(cmd *cobra.Command, args []string) {
		ctx := context.Background()
		config, err := environment.Resolve()
		if err != nil {
			log.Fatal("Error resolving runtime environment: ", err.Error())
		}

		c, err := client.NewInfrastructure(config)
		if err != nil {
			log.Fatal("Error creating infrastructure client: ", err.Error())
		}

		if grantType != "publisher" && grantType != "subscriber" && grantType != "p" && grantType != "s" {
			log.Fatal("grant type must be one of {publisher,subscriber,p,s}")
		}

		passed, err := prompt.ConfirmationCheck("Provide the event type you are removing", "revoke and delete", fmt.Sprintf("auth grant (%s)", grantType), []string{eventType})
		if err != nil {
			log.Fatalf("Failed attempting a confirmation check: %v", err)
		} else if !passed {
			log.Fatal("Aborting due to confirmation failure")
		}

		err = revoke(ctx, c)
		if err != nil {
			log.Fatal("Failed revocation of grant: ", err.Error())
		}

		fmt.Printf("\nRemoving grant now...\n")
		err = delete(ctx, c)
		if err != nil {
			log.Fatal("Failed deletion of grant (warning db may be out of sync with infra and might require a manual fix): ", err.Error())
		}
		output.Newline()
	},
}

func revoke(ctx context.Context, c rpc.Infrastructure) error {
	if strings.ToLower(grantType) == "publisher" || strings.ToLower(grantType) == "p" {
		resp, err := c.RevokeAuthorizedFieldGrant(ctx, &rpc.RevokeAuthorizedFieldGrantReq{
			RevocationType: &rpc.RevokeAuthorizedFieldGrantReq_Publisher{
				Publisher: &rpc.AuthorizedFieldPublisherGrant{
					IamRole:     iamRole,
					EventType:   eventType,
					Environment: env,
				},
			},
		})
		if err != nil {
			return fmt.Errorf("Error revoking publication grant for role (%s), event (%s), env (%s): %v", iamRole, eventType, env, err)
		}

		outputResponse(resp.Revoked, "Revoked publisher grant successfully", "Revocation for publisher grant unsuccessful (w/o error)", map[string]string{
			"IAMRole":     iamRole,
			"Event Type":  eventType,
			"Environment": env,
		})
	} else if strings.ToLower(grantType) == "subscriber" || strings.ToLower(grantType) == "s" {
		if msg == "" || field == "" {
			log.Fatal("Message and Field values must be set when revoking subscriber grant")
		}

		resp, err := c.RevokeAuthorizedFieldGrant(ctx, &rpc.RevokeAuthorizedFieldGrantReq{
			RevocationType: &rpc.RevokeAuthorizedFieldGrantReq_Subscriber{
				Subscriber: &rpc.AuthorizedFieldSubscriberGrant{
					IamRole:     iamRole,
					EventType:   eventType,
					Environment: env,
					AuthorizedField: &rpc.AuthorizedField{
						FieldName:   field,
						MessageName: msg,
					},
				},
			},
		})
		if err != nil {
			return fmt.Errorf("Error revoking subscription grant for role (%s), event (%s), env (%s), field (%s), msg (%s): %v", iamRole, eventType, env, field, msg, err)
		}

		outputResponse(resp.Revoked, "Revoked subscriber grant successfully", "Revocation for subscriber grant unsuccessful (w/o error)", map[string]string{
			"IAMRole":     iamRole,
			"Event Type":  eventType,
			"Environment": env,
			"Field":       field,
			"Message":     msg,
		})
	}
	return nil
}

func delete(ctx context.Context, c rpc.Infrastructure) error {
	if strings.ToLower(grantType) == "publisher" || strings.ToLower(grantType) == "p" {
		resp, err := c.DeleteAuthorizedFieldGrant(ctx, &rpc.DeleteAuthorizedFieldGrantReq{
			Grant: &rpc.DeleteAuthorizedFieldGrantReq_Publisher{
				Publisher: &rpc.AuthorizedFieldPublisherGrant{
					IamRole:     iamRole,
					EventType:   eventType,
					Environment: env,
				},
			},
		})
		if err != nil {
			return fmt.Errorf("Error deleting publication grant for role (%s), event (%s), env (%s): %v", iamRole, eventType, env, err)
		}

		outputResponse(resp.Deleted, "Deleted publisher grant successfully", "Deletion for publisher grant unsuccessful (w/o error)", map[string]string{
			"IAMRole":     iamRole,
			"Event Type":  eventType,
			"Environment": env,
		})
	} else if strings.ToLower(grantType) == "subscriber" || strings.ToLower(grantType) == "s" {
		resp, err := c.DeleteAuthorizedFieldGrant(ctx, &rpc.DeleteAuthorizedFieldGrantReq{
			Grant: &rpc.DeleteAuthorizedFieldGrantReq_Subscriber{
				Subscriber: &rpc.AuthorizedFieldSubscriberGrant{
					IamRole:     iamRole,
					EventType:   eventType,
					Environment: env,
					AuthorizedField: &rpc.AuthorizedField{
						FieldName:   field,
						MessageName: msg,
					},
				},
			},
		})
		if err != nil {
			return fmt.Errorf("Error deleting subscription grant for role (%s), event (%s), env (%s), field (%s), msg (%s): %v", iamRole, eventType, env, field, msg, err)
		}

		outputResponse(resp.Deleted, "Deleted subscriber grant successfully", "deletion for subscriber grant unsuccessful (w/o error)", map[string]string{
			"IAMRole":     iamRole,
			"Event Type":  eventType,
			"Environment": env,
			"Field":       field,
			"Message":     msg,
		})
	}
	return nil
}

func outputResponse(success bool, msgSuccess, msgFailure string, m map[string]string) {
	if success {
		output.MapMsg(msgSuccess, m)
	} else {
		output.MapMsg(msgFailure, m)
	}

}
