package main

import (
	"errors"
	"fmt"
	"strings"

	"github.com/spf13/cobra"

	"github.com/safing/jess"
)

func init() {
	rootCmd.AddCommand(exportCmd)
	rootCmd.AddCommand(backupCmd)
	rootCmd.AddCommand(importCmd)
}

var (
	exportCmdHelp = "usage: export <id>"
	exportCmd     = &cobra.Command{
		Use:   "export <id>",
		Short: "export a signet or envelope",
		Long:  "export a signet (as a recipient - the public key only) or an envelope (configuration)",
		RunE:  handleExport,
	}

	backupCmdHelp = "usage: backup <id"
	backupCmd     = &cobra.Command{
		Use:   "backup <id>",
		Short: "backup a signet",
		Long:  "backup a signet (the private key - do not share!)",
		RunE:  handleBackup,
	}

	importCmdHelp = "usage: import <text>"
	importCmd     = &cobra.Command{
		Use:   "import <text>",
		Short: "import a signet or an enveleope",
		Long:  "import a signet (any kind) or an enveleope",
		RunE:  handleImport,
	}
)

func handleExport(cmd *cobra.Command, args []string) error {
	// Check args.
	if len(args) != 1 {
		return errors.New(exportCmdHelp)
	}
	id := args[0]

	// Get Recipient.
	recipient, err := trustStore.GetSignet(id, true)
	if err == nil {
		text, err := recipient.Export(false)
		if err != nil {
			return fmt.Errorf("failed to export recipient %s: %w", id, err)
		}
		fmt.Println(text)
		return nil
	}

	// Check if there is a signet instead.
	signet, err := trustStore.GetSignet(id, false)
	if err == nil {
		recipient, err := signet.AsRecipient()
		if err != nil {
			return fmt.Errorf("failed convert signet %s to recipient for export: %w", id, err)
		}
		text, err := recipient.Export(false)
		if err != nil {
			return fmt.Errorf("failed to export recipient %s: %w", id, err)
		}
		fmt.Println(text)
		return nil
	}

	// Check for an envelope.
	env, err := trustStore.GetEnvelope(id)
	if err == nil {
		text, err := env.Export(false)
		if err != nil {
			return fmt.Errorf("failed to export envelope %s: %w", id, err)
		}
		fmt.Println(text)
		return nil
	}

	return errors.New("no recipient or envelope found with the given ID")
}

func handleBackup(cmd *cobra.Command, args []string) error {
	// Check args.
	if len(args) != 1 {
		return errors.New(backupCmdHelp)
	}
	id := args[0]

	// Check if there is a signet instead.
	signet, err := trustStore.GetSignet(id, false)
	if err != nil {
		text, err := signet.Backup(false)
		if err != nil {
			return fmt.Errorf("failed to backup signet %s: %w", id, err)
		}
		fmt.Println(text)
		return nil
	}

	return errors.New("no signet found with the given ID")
}

func handleImport(cmd *cobra.Command, args []string) error {
	// Check args.
	if len(args) != 1 {
		return errors.New(importCmdHelp)
	}
	text := args[0]

	// First, check if it's an envelope.
	if strings.HasPrefix(text, jess.ExportEnvelopePrefix) {
		env, err := jess.EnvelopeFromTextFormat(text)
		if err != nil {
			return fmt.Errorf("failed to parse envelope: %w", err)
		}
		err = trustStore.StoreEnvelope(env)
		if err != nil {
			return fmt.Errorf("failed to import envelope into trust store: %w", err)
		}
		fmt.Printf("imported envelope %q intro trust store\n", env.Name)
		return nil
	}

	// Then handle all signet types together.
	var (
		signetType string
		parseFunc  func(textFormat string) (*jess.Signet, error)
	)
	switch {
	case strings.HasPrefix(text, jess.ExportSenderPrefix):
		signetType = jess.ExportSenderKeyword
		parseFunc = jess.SenderFromTextFormat
	case strings.HasPrefix(text, jess.ExportRecipientPrefix):
		signetType = jess.ExportRecipientKeyword
		parseFunc = jess.RecipientFromTextFormat
	case strings.HasPrefix(text, jess.ExportKeyPrefix):
		signetType = jess.ExportKeyKeyword
		parseFunc = jess.KeyFromTextFormat
	default:
		return fmt.Errorf(
			"invalid format or unknown type, expected one of %s, %s, %s, %s",
			jess.ExportKeyKeyword,
			jess.ExportSenderKeyword,
			jess.ExportRecipientKeyword,
			jess.ExportEnvelopeKeyword,
		)
	}
	// Parse and import
	signet, err := parseFunc(text)
	if err != nil {
		return fmt.Errorf("failed to parse %s: %w", signetType, err)
	}
	err = trustStore.StoreSignet(signet)
	if err != nil {
		return fmt.Errorf("failed to import %s into trust store: %w", signetType, err)
	}
	fmt.Printf("imported %s %s intro trust store\n", signetType, signet.ID)

	return nil
}