// Copyright (c) HashiCorp, Inc.
// SPDX-License-Identifier: MPL-2.0

package ui

import (
	"errors"
	"fmt"
	"io"

	"github.com/mitchellh/cli"
)

// WriterUI is an implementation of the cli.Ui interface which can be used for
// commands that need to have direct access to the underlying UI readers and
// writers.
type WriterUI struct {
	// Ui is the wrapped cli.Ui that supplies the functions for the thin shims
	Ui cli.Ui

	reader      io.Reader
	writer      io.Writer
	errorWriter io.Writer

	// baseUi stores the basic UI that was used to create this WriterUI. It
	// allows us to call its functions and not implement them again.
	baseUi cli.Ui
}

// NewWriterUI generates a new cli.Ui that can be used for commands that
// need access to the underlying UI's writers for copying large amounts of
// data without local buffering. The caller is required to pass a UI
// chain ending in a cli.BasicUi (or a cli.MockUi for testing).
//
// Currently, the UIs in the chain need to be pointers to a cli.ColoredUi,
// cli.BasicUi, or cli.MockUi to work correctly.
func NewWriterUI(ui cli.Ui) (*WriterUI, error) {
	var done bool
	wUI := WriterUI{Ui: ui}

	for !done {
		if ui == nil {
			break
		}

		switch u := ui.(type) {
		case *cli.MockUi:
			wUI.reader = u.InputReader
			wUI.writer = u.OutputWriter
			wUI.errorWriter = u.ErrorWriter
			wUI.baseUi = u
			done = true
		case *cli.BasicUi:
			wUI.reader = u.Reader
			wUI.writer = u.Writer
			wUI.errorWriter = u.ErrorWriter
			wUI.baseUi = u
			done = true
		case *cli.ColoredUi:
			ui = u.Ui
		default:
			return nil, fmt.Errorf("writer ui: unsupported Ui type: %T", ui)
		}
	}

	if !done {
		return nil, errors.New("failed to generate command UI")
	}

	return &wUI, nil
}

func (w *WriterUI) InputReader() io.Reader  { return w.reader }
func (w *WriterUI) OutputWriter() io.Writer { return w.writer }
func (w *WriterUI) ErrorWriter() io.Writer  { return w.errorWriter }

func (w *WriterUI) Output(message string) { w.Ui.Output(message) }
func (w *WriterUI) Info(message string)   { w.Ui.Info(message) }
func (w *WriterUI) Warn(message string)   { w.Ui.Warn(message) }
func (w *WriterUI) Error(message string)  { w.Ui.Error(message) }

func (w *WriterUI) Ask(query string) (string, error)       { return w.Ui.Ask(query) }
func (w *WriterUI) AskSecret(query string) (string, error) { return w.Ui.AskSecret(query) }
