// Package console contains utility functions for working with text consoles
package console

import (
	"errors"
	"fmt"
	"math"
	"os"
	"strings"

	"github.com/chzyer/readline"
	"github.com/gookit/color"
	"github.com/nathan-fiscaletti/consolesize-go"
	"golang.org/x/term"
)

// IsTTY will be true if stdout is connected to a true terminal
var IsTTY bool

// isANSI will be true if console supports ANSI escape code. It is for Windows only.
var isANSI bool

// NoColour should be false if you want output to be coloured
var NoColour = false

func init() {
	IsTTY = term.IsTerminal(int(os.Stdout.Fd()))
	isANSI = true
}

// Size returns the width and height of the console in characters
func Size() (int, int) {
	return consolesize.GetConsoleSize()
}

// CountLines returns the number of lines that would be taken up by the given string
func CountLines(input string) int {
	input = color.ClearCode(input)

	if input == "" {
		return 0
	}

	w, _ := Size()

	if w == 0 {
		return 0
	}

	count := 0
	for _, line := range strings.Split(input, "\n") {
		d := int(math.Ceil(float64(len([]rune(line))) / float64(w)))
		if d == 0 {
			d = 1
		}
		count += d
	}

	return count
}

// ClearLine removes all text from the current line and puts the cursor on the left
func ClearLine() {
	if IsTTY && isANSI {
		fmt.Print("\033[G\033[K")
	} else {
		fmt.Println()
	}
}

// ClearLines removes all text from the previous n lines (starting with the current line) and puts the cursor on the left
func ClearLines(n int) {
	if !IsTTY {
		return
	}

	for i := 0; i < n; i++ {
		ClearLine()
		if i < n-1 {
			if IsTTY && isANSI {
				fmt.Print("\033[F")
			}
		}
	}
}

// Ask prints the supplied prompt and then waits for user input which is returned as a string.
func Ask(prompt string) string {
	if !IsTTY {
		panic(errors.New("no interactive terminal detected; try running rain in interactive mode (e.g. without --yes)"))
	}

	rl, err := readline.NewEx(&readline.Config{
		Prompt: prompt + " ",
	})
	if err != nil {
		panic(fmt.Errorf("unable to get user input: %w", err))
	}

	answer, err := rl.Readline()
	if err != nil {
		panic(fmt.Errorf("unable to get user input: %w", err))
	}

	return strings.TrimSpace(answer)
}

// Confirm asks the user for "y" or "n" and returns true if the response was "y".
// defaultYes is used to determine whether (y/N) or (Y/n) is displayed after the prompt.
func Confirm(defaultYes bool, prompt string) bool {
	extra := " (y/N)"

	if defaultYes {
		extra = " (Y/n)"
	}

	answer := Ask(prompt + extra)

	if strings.ToUpper(answer) == "Y" || (defaultYes && answer == "") {
		return true
	}

	return false
}

// Errorf prints a red message to standard out
func Errorf(f string, args ...any) {
	fmt.Println(Red(fmt.Sprintf(f, args...)))
}
