package cmds

import (
	"context"
	"errors"
	"fmt"
	"time"

	"github.com/spf13/cobra"
	"gorm.io/gorm"

	"github.com/pocket-id/pocket-id/backend/internal/bootstrap"
	"github.com/pocket-id/pocket-id/backend/internal/common"
	"github.com/pocket-id/pocket-id/backend/internal/instanceid"
	"github.com/pocket-id/pocket-id/backend/internal/model"
	"github.com/pocket-id/pocket-id/backend/internal/onetimeaccess"
)

var oneTimeAccessTokenCmd = &cobra.Command{
	Use:   "one-time-access-token [username or email]",
	Short: "Generates a one-time access token for the given user",
	Args:  cobra.ExactArgs(1),
	RunE: func(cmd *cobra.Command, args []string) error {
		// Get the username or email of the user
		userArg := args[0]

		// Connect to the database
		db, pg, err := bootstrap.NewDatabase(cmd.Context())
		if err != nil {
			return err
		}

		// Load the user to retrieve the user ID
		var user model.User
		queryCtx, queryCancel := context.WithTimeout(cmd.Context(), 10*time.Second)
		defer queryCancel()
		err = db.
			WithContext(queryCtx).
			Where("username = ? OR email = ?", userArg, userArg).
			First(&user).
			Error
		switch {
		case errors.Is(err, gorm.ErrRecordNotFound):
			return errors.New("user not found")
		case err != nil:
			return fmt.Errorf("failed to query for user: %w", err)
		case user.ID == "":
			return errors.New("invalid user loaded: ID is empty")
		}

		instanceID, err := instanceid.Load(cmd.Context(), db)
		if err != nil {
			return err
		}

		// One-time access tokens are stored in the actor state store
		// The CLI doesn't run the full actor host, so it uses a minimal state store to persist the token directly
		actorStore, err := bootstrap.NewActorStateStore(bootstrap.NewActorsOpts{
			DB:         db,
			Postgres:   pg,
			EnvConfig:  &common.EnvConfig,
			InstanceID: instanceID,
		})
		if err != nil {
			return fmt.Errorf("failed to initialize the actor state store: %w", err)
		}

		// Create a new access token that expires in 1 hour
		tokenCtx, tokenCancel := context.WithTimeout(cmd.Context(), 10*time.Second)
		defer tokenCancel()
		token, _, err := onetimeaccess.StoreToken(tokenCtx, actorStore, user.ID, time.Hour, false)
		if err != nil {
			return fmt.Errorf("failed to create access token: %w", err)
		}

		// Print the result
		fmt.Printf(`A one-time access token valid for 1 hour has been created for "%s".`+"\n", userArg)
		fmt.Printf("Use the following URL to sign in once: %s/lc/%s\n", common.EnvConfig.AppURL, token)

		return nil
	},
}

func init() {
	rootCmd.AddCommand(oneTimeAccessTokenCmd)
}
