package main

import (
	"errors"
	"fmt"
	"strings"

	"github.com/AlecAivazis/survey/v2"

	"github.com/safing/jess"
)

func newEnvelope(name string) (*jess.Envelope, error) {
	// check name
	name = strings.TrimSpace(name)
	if name == "" {
		return nil, errors.New("missing envelope name")
	}

	// start init process
	envelope := jess.NewUnconfiguredEnvelope()
	envelope.Name = name

	// preset menu
	var preset string
	prompt := &survey.Select{
		Message: "Select preset:",
		Options: []string{
			"Encrypt with password",
			"Encrypt with key",
			"Encrypt for someone and sign",
			"Encrypt for someone but don't sign",
			"Sign a file (wrapped)",
			"Sign a file (separate sig)",
		},
	}
	err := survey.AskOne(prompt, &preset, nil)
	if err != nil {
		return nil, err
	}

	switch preset {
	case "Encrypt with password":
		envelope.SuiteID = jess.SuitePassword
		err = selectSignets(envelope, "pw")
	case "Encrypt with key":
		envelope.SuiteID = jess.SuiteKey
		err = selectSignets(envelope, "key")
	case "Encrypt for someone and sign":
		envelope.SuiteID = jess.SuiteComplete
		err = selectSignets(envelope, "recipient")
		if err == nil {
			err = selectSignets(envelope, "sender")
		}
	case "Encrypt for someone but don't sign":
		envelope.SuiteID = jess.SuiteRcptOnly
		err = selectSignets(envelope, "recipient")
	case "Sign a file (wrapped)":
		envelope.SuiteID = jess.SuiteSign
		err = selectSignets(envelope, "sender")
	case "Sign a file (separate sig)":
		envelope.SuiteID = jess.SuiteSignFile
		err = selectSignets(envelope, "sender")
	}
	if err != nil {
		return nil, err
	}

	return envelope, nil
}

func editEnvelope(envelope *jess.Envelope) error {
	for {
		// main menu

		// print envelope status
		envelope.SecurityLevel = 0 // reset
		session, err := envelope.Correspondence(trustStore)
		if err != nil {
			fmt.Printf("Envelope status: %s\n", err)
		} else {
			fmt.Println("Envelope status: valid.")
			envelope.SecurityLevel = session.SecurityLevel
		}

		// sub menu
		var submenu string
		prompt := &survey.Select{
			Message: "Select to edit",
			Options: formatColumns([][]string{
				{"Done", "save and return"},
				{""},
				{"Suite", envelope.SuiteID},
				{"", "provides " + formatRequirements(envelope.Suite().Provides)},
				{"", "and " + formatSecurityLevel(envelope.Suite().SecurityLevel)},
				{"Secrets", formatSignetNames(envelope.Secrets)},
				{"Recipients", formatSignetNames(envelope.Recipients)},
				{"Senders", formatSignetNames(envelope.Senders)},
				{""},
				{"Export", "export to text format"},
				{"Abort", "discard changes and return"},
				{"Delete", "delete and return"},
			}),
			PageSize: 15,
		}
		err = survey.AskOne(prompt, &submenu, nil)
		if err != nil {
			return err
		}

		switch {
		case strings.HasPrefix(submenu, "Done"):
			// Check if the envelope is valid.
			if envelope.SecurityLevel == 0 {
				fmt.Println("Envelope is invalid, please fix before saving.")
				continue
			}
			// Remove and keys and save.
			envelope.CleanSignets()
			return trustStore.StoreEnvelope(envelope)
		case strings.HasPrefix(submenu, "Export"):
			// Check if the envelope is valid.
			if envelope.SecurityLevel == 0 {
				fmt.Println("Envelope is invalid, please fix before exporting.")
				continue
			}
			// Remove keys and export.
			envelope.CleanSignets()
			text, err := envelope.Export(false)
			if err != nil {
				return fmt.Errorf("failed to export: %w", err)
			}
			fmt.Println("Exported envelope:")
			fmt.Println(text)
		case strings.HasPrefix(submenu, "Abort"):
			return nil
		case strings.HasPrefix(submenu, "Delete"):
			return trustStore.DeleteEnvelope(envelope.Name)
		case strings.HasPrefix(submenu, "Suite"):
			err = editEnvelopeSuite(envelope)
		case strings.HasPrefix(submenu, "Secrets"):
			err = selectSignets(envelope, "pw/key")
		case strings.HasPrefix(submenu, "Recipients"):
			err = selectSignets(envelope, "recipient")
		case strings.HasPrefix(submenu, "Senders"):
			err = selectSignets(envelope, "sender")
		}
		if err != nil {
			return err
		}
	}
}

func editEnvelopeSuite(envelope *jess.Envelope) error {
	all := jess.Suites()
	suiteOptions := make([][]string, 0, len(all))
	for _, suite := range all {
		suiteOptions = append(suiteOptions, []string{
			suite.ID,
			"provides " + suite.Provides.ShortString(),
			formatSecurityLevel(suite.SecurityLevel),
			"uses " + strings.Join(suite.Tools, ", "),
			formatSuiteStatus(suite),
		})
	}

	var selectedSuite string
	prompt := &survey.Select{
		Message:  "Select suite",
		Options:  formatColumns(suiteOptions),
		PageSize: 10,
	}
	err := survey.AskOne(prompt, &selectedSuite, nil)
	if err != nil {
		return err
	}

	envelope.SuiteID = strings.Fields(selectedSuite)[0]
	return envelope.ReloadSuite()
}