package cli

import (
	"crypto/rand"
	"fmt"
	"math/big"
	"os"

	"git.eeqj.de/sneak/secret/internal/secret"
	"github.com/spf13/cobra"
	"github.com/tyler-smith/go-bip39"
)

func newGenerateCmd() *cobra.Command {
	cmd := &cobra.Command{
		Use:   "generate",
		Short: "Generate random data",
		Long:  `Generate various types of random data including mnemonics and secrets.`,
	}

	cmd.AddCommand(newGenerateMnemonicCmd())
	cmd.AddCommand(newGenerateSecretCmd())

	return cmd
}

func newGenerateMnemonicCmd() *cobra.Command {
	return &cobra.Command{
		Use:   "mnemonic",
		Short: "Generate a random BIP39 mnemonic phrase",
		Long:  `Generate a cryptographically secure random BIP39 mnemonic phrase that can be used with 'secret init' or 'secret import'.`,
		RunE: func(cmd *cobra.Command, args []string) error {
			cli := NewCLIInstance()
			return cli.GenerateMnemonic()
		},
	}
}

func newGenerateSecretCmd() *cobra.Command {
	cmd := &cobra.Command{
		Use:   "secret <name>",
		Short: "Generate a random secret and store it in the vault",
		Long:  `Generate a cryptographically secure random secret and store it in the current vault under the given name.`,
		Args:  cobra.ExactArgs(1),
		RunE: func(cmd *cobra.Command, args []string) error {
			length, _ := cmd.Flags().GetInt("length")
			secretType, _ := cmd.Flags().GetString("type")
			force, _ := cmd.Flags().GetBool("force")

			cli := NewCLIInstance()
			return cli.GenerateSecret(args[0], length, secretType, force)
		},
	}

	cmd.Flags().IntP("length", "l", 16, "Length of the generated secret (default 16)")
	cmd.Flags().StringP("type", "t", "base58", "Type of secret to generate (base58, alnum)")
	cmd.Flags().BoolP("force", "f", false, "Overwrite existing secret")

	return cmd
}

// GenerateMnemonic generates a random BIP39 mnemonic phrase
func (cli *CLIInstance) GenerateMnemonic() error {
	// Generate 128 bits of entropy for a 12-word mnemonic
	entropy, err := bip39.NewEntropy(128)
	if err != nil {
		return fmt.Errorf("failed to generate entropy: %w", err)
	}

	// Create mnemonic from entropy
	mnemonic, err := bip39.NewMnemonic(entropy)
	if err != nil {
		return fmt.Errorf("failed to generate mnemonic: %w", err)
	}

	// Output mnemonic to stdout
	fmt.Println(mnemonic)

	// Output helpful information to stderr
	fmt.Fprintln(os.Stderr, "")
	fmt.Fprintln(os.Stderr, "⚠️  IMPORTANT: Save this mnemonic phrase securely!")
	fmt.Fprintln(os.Stderr, "   • Write it down on paper and store it safely")
	fmt.Fprintln(os.Stderr, "   • Do not store it digitally or share it with anyone")
	fmt.Fprintln(os.Stderr, "   • You will need this phrase to recover your secrets")
	fmt.Fprintln(os.Stderr, "   • If you lose this phrase, your secrets cannot be recovered")
	fmt.Fprintln(os.Stderr, "")
	fmt.Fprintln(os.Stderr, "Use this mnemonic with:")
	fmt.Fprintln(os.Stderr, "   secret init    (to initialize a new secret manager)")
	fmt.Fprintln(os.Stderr, "   secret import  (to import into an existing vault)")

	return nil
}

// GenerateSecret generates a random secret and stores it in the vault
func (cli *CLIInstance) GenerateSecret(secretName string, length int, secretType string, force bool) error {
	if length < 1 {
		return fmt.Errorf("length must be at least 1")
	}

	var secretValue string
	var err error

	switch secretType {
	case "base58":
		secretValue, err = generateRandomBase58(length)
	case "alnum":
		secretValue, err = generateRandomAlnum(length)
	case "mnemonic":
		return fmt.Errorf("mnemonic type not supported for secret generation, use 'secret generate mnemonic' instead")
	default:
		return fmt.Errorf("unsupported type: %s (supported: base58, alnum)", secretType)
	}

	if err != nil {
		return fmt.Errorf("failed to generate random secret: %w", err)
	}

	// Store the secret in the vault
	vault, err := secret.GetCurrentVault(cli.fs, cli.stateDir)
	if err != nil {
		return err
	}

	if err := vault.AddSecret(secretName, []byte(secretValue), force); err != nil {
		return err
	}

	fmt.Printf("Generated and stored %d-character %s secret: %s\n", length, secretType, secretName)
	return nil
}

// generateRandomBase58 generates a random base58 string of the specified length
func generateRandomBase58(length int) (string, error) {
	const base58Chars = "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz"
	return generateRandomString(length, base58Chars)
}

// generateRandomAlnum generates a random alphanumeric string of the specified length
func generateRandomAlnum(length int) (string, error) {
	const alnumChars = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"
	return generateRandomString(length, alnumChars)
}

// generateRandomString generates a random string of the specified length using the given character set
func generateRandomString(length int, charset string) (string, error) {
	if length <= 0 {
		return "", fmt.Errorf("length must be positive")
	}

	result := make([]byte, length)
	charsetLen := big.NewInt(int64(len(charset)))

	for i := 0; i < length; i++ {
		randomIndex, err := rand.Int(rand.Reader, charsetLen)
		if err != nil {
			return "", fmt.Errorf("failed to generate random number: %w", err)
		}
		result[i] = charset[randomIndex.Int64()]
	}

	return string(result), nil
}