package main

import (
	"bufio"
	"bytes"
	"errors"
	"fmt"
	"strings"
	"text/tabwriter"

	"github.com/safing/jess"
	"github.com/safing/jess/tools"
)

func formatColumns(table [][]string) []string {
	buf := bytes.NewBuffer(nil)

	// format table with tab writer
	tabWriter := tabwriter.NewWriter(buf, 8, 4, 3, ' ', 0)
	for i := 0; i < len(table); i++ {
		if i > 0 {
			// linebreak
			fmt.Fprint(tabWriter, "\n")
		}
		fmt.Fprint(tabWriter, strings.Join(table[i], "\t"))
	}
	_ = tabWriter.Flush()

	// parse to []string
	var lines []string
	scanner := bufio.NewScanner(buf)
	for scanner.Scan() {
		lines = append(lines, scanner.Text())
	}
	if err := scanner.Err(); err != nil {
		return nil
	}

	return lines
}

func formatSecurityLevel(securityLevel int) string {
	return fmt.Sprintf("%d b/s", securityLevel)
}

func formatToolSecurityLevel(tool *tools.Tool) string {
	if tool.Info.HasOption(tools.OptionNeedsSecurityLevel) {
		return "dynamic b/s (set manually via --seclevel)"
	}
	if tool.Info.SecurityLevel == 0 {
		return ""
	}
	return formatSecurityLevel(tool.Info.SecurityLevel)
}

func formatSignetName(signet *jess.Signet) string {
	switch {
	case signet.Info != nil && signet.Info.Name != "":
		return signet.Info.Name
	case signet.ID != "":
		return signet.ID
	default:
		return "[unknown]"
	}
}

func formatSignetType(signet *jess.Signet) string {
	switch {
	case signet.Scheme == jess.SignetSchemeKey:
		return "key"
	case signet.Scheme == jess.SignetSchemePassword:
		return "password"
	case signet.Public:
		return "recipient"
	default:
		return "signet"
	}
}

func formatSignetScheme(signet *jess.Signet) string {
	switch signet.Scheme {
	case jess.SignetSchemeKey, jess.SignetSchemePassword:
		return ""
	default:
		return signet.Scheme
	}
}

func formatSignetPurpose(signet *jess.Signet) string {
	switch signet.Scheme {
	case jess.SignetSchemeKey, jess.SignetSchemePassword:
		return ""
	}

	tool, err := signet.Tool()
	if err != nil {
		return "[unknown]"
	}
	return tool.Info.FormatPurpose()
}

func formatSignetSecurityLevel(signet *jess.Signet) string {
	switch signet.Scheme {
	case jess.SignetSchemeKey, jess.SignetSchemePassword:
		return ""
	}

	tool, err := signet.Tool()
	if err != nil {
		return failPlaceholder
	}

	securityLevel, err := tool.StaticLogic.SecurityLevel(signet)
	if err != nil {
		if errors.Is(err, tools.ErrProtected) {
			return "[protected]"
		}
		return failPlaceholder
	}

	return fmt.Sprintf("%d b/s", securityLevel)
}

func formatRequirements(reqs *jess.Requirements) string {
	if reqs == nil || reqs.Empty() {
		return "none (unsafe)"
	}
	return reqs.String()
}

func formatSignetNames(signets []*jess.Signet) string {
	names := make([]string, 0, len(signets))
	for _, signet := range signets {
		names = append(names, formatSignetName(signet))
	}
	return strings.Join(names, ", ")
}

func formatEnvelopeSignets(envelope *jess.Envelope) string {
	var sections []string
	if len(envelope.Secrets) > 0 {
		sections = append(sections, fmt.Sprintf("Secrets: %s", formatSignetNames(envelope.Secrets)))
	}
	if len(envelope.Recipients) > 0 {
		sections = append(sections, fmt.Sprintf("To: %s", formatSignetNames(envelope.Recipients)))
	}
	if len(envelope.Senders) > 0 {
		sections = append(sections, fmt.Sprintf("From: %s", formatSignetNames(envelope.Senders)))
	}
	return strings.Join(sections, ", ")
}

func formatSuiteStatus(suite *jess.Suite) string {
	switch suite.Status {
	case jess.SuiteStatusDeprecated:
		return "DEPRECATED"
	case jess.SuiteStatusRecommended:
		return "recommended"
	default:
		return ""
	}
}