package trust

import (
	"context"
	"fmt"
	"io"
	"sort"

	"github.com/docker/cli/cli/command"
	"github.com/docker/cli/cli/command/formatter"
	"github.com/fvbommel/sortorder"
	"github.com/theupdateframework/notary/client"
)

func prettyPrintTrustInfo(ctx context.Context, dockerCLI command.Cli, remote string) error {
	signatureRows, adminRolesWithSigs, delegationRoles, err := lookupTrustInfo(ctx, dockerCLI, remote)
	if err != nil {
		return err
	}

	if len(signatureRows) > 0 {
		_, _ = fmt.Fprintf(dockerCLI.Out(), "\nSignatures for %s\n\n", remote)

		if err := printSignatures(dockerCLI.Out(), signatureRows); err != nil {
			return err
		}
	} else {
		_, _ = fmt.Fprintf(dockerCLI.Out(), "\nNo signatures for %s\n\n", remote)
	}
	signerRoleToKeyIDs := getDelegationRoleToKeyMap(delegationRoles)

	// If we do not have additional signers, do not display
	if len(signerRoleToKeyIDs) > 0 {
		_, _ = fmt.Fprintf(dockerCLI.Out(), "\nList of signers and their keys for %s\n\n", remote)
		if err := printSignerInfo(dockerCLI.Out(), signerRoleToKeyIDs); err != nil {
			return err
		}
	}

	// This will always have the root and targets information
	_, _ = fmt.Fprintf(dockerCLI.Out(), "\nAdministrative keys for %s\n\n", remote)
	printSortedAdminKeys(dockerCLI.Out(), adminRolesWithSigs)
	return nil
}

func printSortedAdminKeys(out io.Writer, adminRoles []client.RoleWithSignatures) {
	sort.Slice(adminRoles, func(i, j int) bool { return adminRoles[i].Name > adminRoles[j].Name })
	for _, adminRole := range adminRoles {
		if formattedAdminRole := formatAdminRole(adminRole); formattedAdminRole != "" {
			_, _ = fmt.Fprintf(out, "  %s", formattedAdminRole)
		}
	}
}

// pretty print with ordered rows
func printSignatures(out io.Writer, signatureRows []trustTagRow) error {
	trustTagCtx := formatter.Context{
		Output: out,
		Format: defaultTrustTagTableFormat,
	}
	// convert the formatted type before printing
	formattedTags := []signedTagInfo{}
	for _, sigRow := range signatureRows {
		formattedSigners := sigRow.Signers
		if len(formattedSigners) == 0 {
			formattedSigners = append(formattedSigners, fmt.Sprintf("(%s)", releasedRoleName))
		}
		formattedTags = append(formattedTags, signedTagInfo{
			Name:    sigRow.SignedTag,
			Digest:  sigRow.Digest,
			Signers: formattedSigners,
		})
	}
	return tagWrite(trustTagCtx, formattedTags)
}

func printSignerInfo(out io.Writer, roleToKeyIDs map[string][]string) error {
	signerInfoCtx := formatter.Context{
		Output: out,
		Format: defaultSignerInfoTableFormat,
		Trunc:  true,
	}
	formattedSignerInfo := []signerInfo{}
	for name, keyIDs := range roleToKeyIDs {
		formattedSignerInfo = append(formattedSignerInfo, signerInfo{
			Name: name,
			Keys: keyIDs,
		})
	}
	sort.Slice(formattedSignerInfo, func(i, j int) bool {
		return sortorder.NaturalLess(formattedSignerInfo[i].Name, formattedSignerInfo[j].Name)
	})
	return signerInfoWrite(signerInfoCtx, formattedSignerInfo)
}
