// Copyright 2021 The age Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package main

// This file implements the terminal UI of cmd/age. The rules are:
//
//   - Anything that requires user interaction goes to the terminal,
//     and is erased afterwards if possible. This UI would be possible
//     to replace with a pinentry with no output or UX changes.
//
//   - Everything else goes to standard error with an "age:" prefix.
//     No capitalized initials and no periods at the end.
//
// The one exception is the autogenerated passphrase, which goes to
// the terminal, since we really want it to reach the user only.

import (
	"bytes"
	"fmt"
	"io"
	"log"
	"os"

	"filippo.io/age/armor"
	"filippo.io/age/internal/term"
)

// l is a logger with no prefixes.
var l = log.New(os.Stderr, "", 0)

func printf(format string, v ...any) {
	l.Printf("age: "+format, v...)
}

func errorf(format string, v ...any) {
	l.Printf("age: error: "+format, v...)
	l.Printf("age: report unexpected or unhelpful errors at https://filippo.io/age/report")
	os.Exit(1)
}

func warningf(format string, v ...any) {
	l.Printf("age: warning: "+format, v...)
}

func errorWithHint(error string, hints ...string) {
	l.Printf("age: error: %s", error)
	for _, hint := range hints {
		l.Printf("age: hint: %s", hint)
	}
	l.Printf("age: report unexpected or unhelpful errors at https://filippo.io/age/report")
	os.Exit(1)
}

func printfToTerminal(format string, v ...any) error {
	return term.WithTerminal(func(_, out *os.File) error {
		_, err := fmt.Fprintf(out, "age: "+format+"\n", v...)
		return err
	})
}

func bufferTerminalInput(in io.Reader) (io.Reader, error) {
	buf := &bytes.Buffer{}
	if _, err := buf.ReadFrom(ReaderFunc(func(p []byte) (n int, err error) {
		if bytes.Contains(buf.Bytes(), []byte(armor.Footer+"\n")) {
			return 0, io.EOF
		}
		return in.Read(p)
	})); err != nil {
		return nil, err
	}
	return buf, nil
}

type ReaderFunc func(p []byte) (n int, err error)

func (f ReaderFunc) Read(p []byte) (n int, err error) { return f(p) }
