// SPDX-License-Identifier: GPL-3.0-or-later

// Package output provides output rendering utilities for the CLI.
package output

import (
	"encoding/json"
	"fmt"
	"io"
	"os"
	"strings"

	"github.com/privatebackup/client-frontend-unix/internal/term"
)

// Format represents the output format.
type Format string

const (
	// FormatHuman is human-readable output (default).
	FormatHuman Format = "human"
	// FormatJSON is JSON output for scripting.
	FormatJSON Format = "json"
	// FormatPlain is plain text output without formatting.
	FormatPlain Format = "plain"
)

// Renderer handles output formatting and rendering.
type Renderer struct {
	writer    io.Writer
	errWriter io.Writer
	format    Format
	terminal  *term.Terminal
	colors    *Colors
	quiet     bool
	verbose   bool
}

// Option configures a Renderer.
type Option func(*Renderer)

// NewRenderer creates a new renderer with the given options.
func NewRenderer(opts ...Option) *Renderer {
	r := &Renderer{
		writer:    os.Stdout,
		errWriter: os.Stderr,
		format:    FormatHuman,
		terminal:  term.Detect(),
	}

	for _, opt := range opts {
		opt(r)
	}

	// Initialize colors based on terminal and settings
	if r.colors == nil {
		r.colors = NewColors(r.terminal.HasColors)
	}

	return r
}

// WithWriter sets the output writer.
func WithWriter(w io.Writer) Option {
	return func(r *Renderer) {
		r.writer = w
	}
}

// WithErrWriter sets the error output writer.
func WithErrWriter(w io.Writer) Option {
	return func(r *Renderer) {
		r.errWriter = w
	}
}

// WithFormat sets the output format.
func WithFormat(format Format) Option {
	return func(r *Renderer) {
		r.format = format
	}
}

// WithTerminal sets the terminal configuration.
func WithTerminal(t *term.Terminal) Option {
	return func(r *Renderer) {
		r.terminal = t
	}
}

// WithQuiet enables quiet mode (suppress non-essential output).
func WithQuiet(quiet bool) Option {
	return func(r *Renderer) {
		r.quiet = quiet
	}
}

// WithVerbose enables verbose output.
func WithVerbose(verbose bool) Option {
	return func(r *Renderer) {
		r.verbose = verbose
	}
}

// WithNoColor disables color output.
func WithNoColor() Option {
	return func(r *Renderer) {
		r.colors = NewColors(false)
	}
}

// Format returns the current output format.
func (r *Renderer) Format() Format {
	return r.format
}

// IsQuiet returns whether quiet mode is enabled.
func (r *Renderer) IsQuiet() bool {
	return r.quiet
}

// IsVerbose returns whether verbose mode is enabled.
func (r *Renderer) IsVerbose() bool {
	return r.verbose
}

// Terminal returns the terminal configuration.
func (r *Renderer) Terminal() *term.Terminal {
	return r.terminal
}

// Colors returns the color configuration.
func (r *Renderer) Colors() *Colors {
	return r.colors
}

// Writer returns the output writer.
func (r *Renderer) Writer() io.Writer {
	return r.writer
}

// Output outputs data in the appropriate format.
// For JSON format, it outputs the data parameter.
// For human format, it calls the humanFn function.
func (r *Renderer) Output(data interface{}, humanFn func()) {
	switch r.format {
	case FormatJSON:
		r.JSON(data)
	default:
		humanFn()
	}
}

// JSON outputs data as JSON.
func (r *Renderer) JSON(data interface{}) {
	enc := json.NewEncoder(r.writer)
	enc.SetIndent("", "  ")
	_ = enc.Encode(data)
}

// Printf prints formatted text.
func (r *Renderer) Printf(format string, args ...interface{}) {
	fmt.Fprintf(r.writer, format, args...)
}

// Println prints a line of text.
func (r *Renderer) Println(args ...interface{}) {
	fmt.Fprintln(r.writer, args...)
}

// Print prints text.
func (r *Renderer) Print(args ...interface{}) {
	fmt.Fprint(r.writer, args...)
}

// Newline prints an empty line.
func (r *Renderer) Newline() {
	fmt.Fprintln(r.writer)
}

// Section prints a section header.
func (r *Renderer) Section(title string) {
	if r.quiet {
		return
	}
	if r.format == FormatJSON {
		return
	}
	fmt.Fprintf(r.writer, "\n%s\n", r.colors.Bold(title))
}

// Subsection prints a subsection header.
func (r *Renderer) Subsection(title string) {
	if r.quiet {
		return
	}
	if r.format == FormatJSON {
		return
	}
	fmt.Fprintf(r.writer, "\n%s\n", title)
}

// Info prints an informational message.
func (r *Renderer) Info(msg string) {
	if r.quiet {
		return
	}
	if r.format == FormatJSON {
		return
	}
	fmt.Fprintf(r.writer, "%s %s\n", r.colors.Info("ℹ"), msg)
}

// Success prints a success message.
func (r *Renderer) Success(msg string) {
	if r.quiet {
		return
	}
	if r.format == FormatJSON {
		return
	}
	fmt.Fprintf(r.writer, "%s %s\n", r.colors.Success("✓"), msg)
}

// Warning prints a warning message.
func (r *Renderer) Warning(msg string) {
	if r.format == FormatJSON {
		return
	}
	fmt.Fprintf(r.errWriter, "%s %s\n", r.colors.Warning("⚠"), msg)
}

// Error prints an error message.
func (r *Renderer) Error(msg string) {
	if r.format == FormatJSON {
		return
	}
	fmt.Fprintf(r.errWriter, "%s %s\n", r.colors.Error("✗"), msg)
}

// Errorf prints a formatted error message.
func (r *Renderer) Errorf(format string, args ...interface{}) {
	r.Error(fmt.Sprintf(format, args...))
}

// Debug prints a debug message (only in verbose mode).
func (r *Renderer) Debug(msg string) {
	if !r.verbose {
		return
	}
	if r.format == FormatJSON {
		return
	}
	fmt.Fprintf(r.writer, "%s %s\n", r.colors.Muted("[DEBUG]"), msg)
}

// Debugf prints a formatted debug message (only in verbose mode).
func (r *Renderer) Debugf(format string, args ...interface{}) {
	r.Debug(fmt.Sprintf(format, args...))
}

// Verbose prints a message only in verbose mode.
func (r *Renderer) Verbose(msg string) {
	if !r.verbose {
		return
	}
	if r.format == FormatJSON {
		return
	}
	fmt.Fprintf(r.writer, "%s\n", msg)
}

// List prints a list of items.
func (r *Renderer) List(items []string) {
	if r.format == FormatJSON {
		r.JSON(items)
		return
	}
	for _, item := range items {
		fmt.Fprintf(r.writer, "  • %s\n", item)
	}
}

// KeyValue prints a key-value pair.
func (r *Renderer) KeyValue(key, value string) {
	if r.format == FormatJSON {
		return
	}
	fmt.Fprintf(r.writer, "  %s: %s\n", r.colors.Muted(key), value)
}

// KeyValueList prints multiple key-value pairs.
func (r *Renderer) KeyValueList(pairs map[string]string) {
	if r.format == FormatJSON {
		r.JSON(pairs)
		return
	}

	// Find max key length for alignment
	maxLen := 0
	for key := range pairs {
		if len(key) > maxLen {
			maxLen = len(key)
		}
	}

	for key, value := range pairs {
		padding := strings.Repeat(" ", maxLen-len(key))
		fmt.Fprintf(r.writer, "  %s%s: %s\n", r.colors.Muted(key), padding, value)
	}
}

// Progress returns a new progress bar.
func (r *Renderer) Progress(total int64, description string) *ProgressBar {
	pb := NewProgressBar(r.writer, r.colors,
		WithTTY(r.terminal.IsTTY),
		WithUnicode(r.terminal.SupportsUnicode()),
	)
	pb.Start(total)
	pb.SetMessage(description)
	return pb
}

// Spinner returns a new spinner.
func (r *Renderer) Spinner(message string) *Spinner {
	s := NewSpinner(r.writer, r.colors)
	s.SetMessage(message)
	return s
}

// Prompt prints a prompt message and returns whether the response is affirmative.
func (r *Renderer) Prompt(msg string) {
	fmt.Fprintf(r.writer, "%s ", msg)
}

// StatusLine prints a status line that can be updated.
func (r *Renderer) StatusLine(status, message string) {
	if r.quiet || r.format == FormatJSON {
		return
	}

	var statusColor func(string) string
	switch status {
	case "ok", "success", "running":
		statusColor = r.colors.Success
	case "warn", "warning":
		statusColor = r.colors.Warning
	case "error", "failed":
		statusColor = r.colors.Error
	default:
		statusColor = r.colors.Info
	}

	fmt.Fprintf(r.writer, "  [%s] %s\n", statusColor(status), message)
}

// Separator prints a horizontal separator.
func (r *Renderer) Separator() {
	if r.quiet || r.format == FormatJSON {
		return
	}
	width := r.terminal.GetWidth()
	if width > 80 {
		width = 80
	}
	fmt.Fprintln(r.writer, strings.Repeat("─", width))
}

// Heading prints a prominent heading.
func (r *Renderer) Heading(title string) {
	if r.quiet || r.format == FormatJSON {
		return
	}
	fmt.Fprintf(r.writer, "\n%s\n", r.colors.Bold(title))
	fmt.Fprintln(r.writer, strings.Repeat("─", len(title)))
}

// Box prints text in a box.
func (r *Renderer) Box(title string, lines ...string) {
	if r.quiet || r.format == FormatJSON {
		return
	}
	fmt.Fprintln(r.writer)
	fmt.Fprintf(r.writer, "┌─ %s ─┐\n", title)
	for _, line := range lines {
		fmt.Fprintf(r.writer, "│ %s │\n", line)
	}
	fmt.Fprintln(r.writer, "└────────────┘")
}

// ErrorWithDetails prints an error with additional details.
func (r *Renderer) ErrorWithDetails(msg string, details ...string) {
	r.Error(msg)
	for _, detail := range details {
		fmt.Fprintf(r.errWriter, "  %s\n", detail)
	}
}
