package cli

import (
	"bufio"
	"fmt"
	"os"
	"strings"
	"syscall"

	"git.eeqj.de/sneak/secret/internal/secret"
	"github.com/spf13/afero"
	"github.com/spf13/cobra"
	"golang.org/x/term"
)

// Global scanner for consistent stdin reading
var stdinScanner *bufio.Scanner //nolint:gochecknoglobals // Needed for consistent stdin handling

// Instance encapsulates all CLI functionality and state
type Instance struct {
	fs       afero.Fs
	stateDir string
	cmd      *cobra.Command
}

// NewCLIInstance creates a new CLI instance with the real filesystem
func NewCLIInstance() *Instance {
	fs := afero.NewOsFs()
	stateDir := secret.DetermineStateDir("")
	return &Instance{
		fs:       fs,
		stateDir: stateDir,
	}
}

// NewCLIInstanceWithFs creates a new CLI instance with the given filesystem (for testing)
func NewCLIInstanceWithFs(fs afero.Fs) *Instance {
	stateDir := secret.DetermineStateDir("")
	return &Instance{
		fs:       fs,
		stateDir: stateDir,
	}
}

// NewCLIInstanceWithStateDir creates a new CLI instance with custom state directory (for testing)
func NewCLIInstanceWithStateDir(fs afero.Fs, stateDir string) *Instance {
	return &Instance{
		fs:       fs,
		stateDir: stateDir,
	}
}

// SetFilesystem sets the filesystem for this CLI instance (for testing)
func (cli *Instance) SetFilesystem(fs afero.Fs) {
	cli.fs = fs
}

// SetStateDir sets the state directory for this CLI instance (for testing)
func (cli *Instance) SetStateDir(stateDir string) {
	cli.stateDir = stateDir
}

// GetStateDir returns the state directory for this CLI instance
func (cli *Instance) GetStateDir() string {
	return cli.stateDir
}

// getStdinScanner returns a shared scanner for stdin to avoid buffering issues
func getStdinScanner() *bufio.Scanner {
	if stdinScanner == nil {
		stdinScanner = bufio.NewScanner(os.Stdin)
	}
	return stdinScanner
}

// readLineFromStdin reads a single line from stdin with a prompt
// Uses a shared scanner to avoid buffering issues between multiple calls
func readLineFromStdin(prompt string) (string, error) {
	// Check if stderr is a terminal - if not, we can't prompt interactively
	if !term.IsTerminal(syscall.Stderr) {
		return "", fmt.Errorf("cannot prompt for input: stderr is not a terminal (running in non-interactive mode)")
	}

	fmt.Fprint(os.Stderr, prompt) // Write prompt to stderr, not stdout
	scanner := getStdinScanner()
	if !scanner.Scan() {
		if err := scanner.Err(); err != nil {
			return "", fmt.Errorf("failed to read from stdin: %w", err)
		}
		return "", fmt.Errorf("failed to read from stdin: EOF")
	}
	return strings.TrimSpace(scanner.Text()), nil
}