package setupcmd

import (
	"context"
	"encoding/json"
	"fmt"
	"io"

	"github.com/nexuscrm/persistence"
)

const permissionStateInitializationCommand = "ensure-permission-state"

type permissionStateInitializer interface {
	EnsureInitialized(context.Context) (persistence.PermissionState, error)
}

type permissionStateInitializerFactory func() (permissionStateInitializer, error)

type permissionStateInitializationOutput struct {
	Revision        uint64 `json:"revision"`
	EnforcementMode string `json:"enforcementMode"`
}

func runPermissionStateInitializationCommand(
	ctx context.Context,
	args []string,
	stdout io.Writer,
	stderr io.Writer,
	factory permissionStateInitializerFactory,
) int {
	if len(args) != 0 {
		fmt.Fprintf(stderr, "%s: unexpected arguments: %v\n", permissionStateInitializationCommand, args)
		return 2
	}
	if ctx == nil {
		fmt.Fprintf(stderr, "%s: context is required\n", permissionStateInitializationCommand)
		return 1
	}
	if factory == nil {
		fmt.Fprintf(stderr, "%s: repository factory is required\n", permissionStateInitializationCommand)
		return 1
	}
	initializer, err := factory()
	if err != nil {
		fmt.Fprintf(stderr, "%s: open repository: %v\n", permissionStateInitializationCommand, err)
		return 1
	}
	if initializer == nil {
		fmt.Fprintf(stderr, "%s: open repository: initializer is unavailable\n", permissionStateInitializationCommand)
		return 1
	}
	state, err := initializer.EnsureInitialized(ctx)
	if err != nil {
		fmt.Fprintf(stderr, "%s: initialize permission state: %v\n", permissionStateInitializationCommand, err)
		return 1
	}
	if err := state.Validate(); err != nil {
		fmt.Fprintf(stderr, "%s: initialized state is invalid: %v\n", permissionStateInitializationCommand, err)
		return 1
	}
	if err := json.NewEncoder(stdout).Encode(permissionStateInitializationOutput{
		Revision:        state.Version.Revision(),
		EnforcementMode: string(state.EnforcementMode),
	}); err != nil {
		fmt.Fprintf(stderr, "%s: write result: %v\n", permissionStateInitializationCommand, err)
		return 1
	}
	return 0
}
