package repl

import (
	
	
	
	
	
	
	
	
	

	
	
	
	

	amhelp 
	am 
	
	
)

var (
	historyPath = ""
	ss          = states.ReplStates
)

type completionFunc func(
	cmd *cobra.Command, args []string, toComplete string,
) ([]string, cobra.ShellCompDirective)

func init() {
	,  := user.Current()
	 := .HomeDir
	historyPath =  + "/.arpc_history"
}

// ///// ///// /////

// ///// ERRORS

// ///// ///// /////

// sentinel errors

var ErrSyntax = errors.New("syntax error")

// error mutations

// AddErrSyntax wraps an error in the ErrSyntax sentinel and adds to a machine.
func (
	 *am.Event,  *am.Machine,  error,  am.A,
) error {
	 = fmt.Errorf("%w: %w", ErrSyntax, )
	.EvAddErrState(, ss.ErrSyntax, , )

	return 
}

// ///// ///// /////

// ///// ARGS

// ///// ///// /////

const APrefix = "am_repl"

// A is a struct for node arguments. It's a typesafe alternative to [am.A].
type A struct {
	Id     string   `log:"id"`
	Addrs  []string `log:"addr"`
	Lines  []string
	States []string `log:"states"`
	// machine ID
	MachId string `log:"mach"`
	// list of machine IDs
	MachIds []string
	// Mutation arguments passed from a Cobra cmd handler.
	MutArgs [2][]string
	// Arguments passed to the CLI from the shell
	CliArgs []string
	// RpcCh is a return channel for a list of [rpc.NetworkMachine]. It has to be
	// buffered or the mutation will fail.
	RpcCh       chan<- []*rpc.Client
	ListFilters *ListFilters
}

// TODO merge with pkg/pubsub and pkg/integrations
// TODO extract to pkg/helpers.Group
type ListFilters struct {
	// ID and hierarchy

	IdExact  string
	IdSubstr string
	IdRegexp *regexp.Regexp
	IdPrefix string
	IdSuffix string
	Parent   string

	// mtime

	MtimeMin    uint64
	MtimeMax    uint64
	MtimeStates S

	// states

	StatesActive   S
	StatesInactive S

	// pagination

	Limit    int
	StartIdx int

	// tags

	// TODO tagRe
	// TODO tagKey
	// TODO tagVal

	// RPC TODO keep only for REPL

	// Include never connected machines
	NoSchema bool
	// Exclude disconnected machines
	SkipDisconn bool
}

// ParseArgs extracts A from [am.Event.Args][APrefix].
func ( am.A) *A {
	if ,  := [APrefix].(*A);  != nil {
		return 
	}
	return &A{}
}

// Pass prepares [am.A] from A to pass to further mutations.
func ( *A) am.A {
	return am.A{APrefix: }
}

// LogArgs is an args logger for A and [rpc.A].
func ( am.A) map[string]string {
	 := rpc.ParseArgs()
	 := ParseArgs()
	if  == nil &&  == nil {
		return nil
	}

	return am.AMerge(amhelp.ArgsToLogMap(, 0), amhelp.ArgsToLogMap(, 0))
}

func completionsNarrowDown(
	 string,  []string,
) ([]string, cobra.ShellCompDirective) {
	// prefix-filtering (case insensitive)
	// TODO update mutation args
	if  != "" {
		 := []string{}
		for ,  := range  {
			 := strings.ToLower()
			 := strings.ToLower()
			if strings.HasPrefix(, ) {
				 = append(, )
			}
		}
		return , cobra.ShellCompDirectiveNoFileComp
	}

	return , cobra.ShellCompDirectiveNoFileComp
}

// ///// ///// /////

// ///// HISTORY

// ///// ///// /////

var (
	errOpenHistoryFile = errors.New("failed to open history file")
	errNegativeIndex   = errors.New(
		"cannot use a negative index when requesting historic commands")
	errOutOfRangeIndex = errors.New(
		"index requested greater than number of items in history")
)

type History struct {
	file  string
	lines []HistoryItem
}

type HistoryItem struct {
	Index    int
	DateTime time.Time
	Block    string
}

// NewSourceFromFile returns a new history source writing to and reading from
// a file.
func historyFromFile( string) (readline.History, error) {
	var  error

	 := new(History)
	.file = 
	.lines,  = openHist()

	return , 
}

func openHist( string) ( []HistoryItem,  error) {
	,  := os.Open()
	if  != nil {
		return , fmt.Errorf("error opening history file: %s", .Error())
	}

	 := bufio.NewScanner()
	for .Scan() {
		var  HistoryItem

		 := json.Unmarshal(.Bytes(), &)
		if  != nil || len(.Block) == 0 {
			continue
		}

		.Index = len()
		 = append(, )
	}

	.Close()

	return , nil
}

// Write item to history file.
func ( *History) ( string) (int, error) {
	 := strings.TrimSpace()
	if  == "" {
		return 0, nil
	}

	 := HistoryItem{
		DateTime: time.Now(),
		Block:    ,
		Index:    len(.lines),
	}

	if len(.lines) == 0 || .lines[len(.lines)-1].Block !=  {
		.lines = append(.lines, )
	}

	 := struct {
		 time.Time `json:"datetime"`
		    string    `json:"block"`
	}{
		:    ,
		: .DateTime,
	}

	,  := json.Marshal()
	if  != nil {
		return .Len(), 
	}

	// TODO cache the FD
	,  := os.OpenFile(.file, os.O_CREATE|os.O_APPEND|os.O_WRONLY, 0o666)
	if  != nil {
		return 0, fmt.Errorf("%w: %s", errOpenHistoryFile, .Error())
	}

	_, _ = .Write(append(, '\n'))
	.Close()

	return .Len(), nil
}

// GetLine returns a specific line from the history file.
func ( *History) ( int) (string, error) {
	if  < 0 {
		return "", errNegativeIndex
	}

	if  < len(.lines) {
		return .lines[].Block, nil
	}

	return "", errOutOfRangeIndex
}

// Len returns the number of items in the history file.
func ( *History) () int {
	return len(.lines)
}

// Dump returns the entire history file.
func ( *History) () interface{} {
	return .lines
}

// ///// ///// /////

// ///// FUNCS

// ///// ///// /////

// setupPrompt is a function which sets up the prompts for the main menu.
func setupPrompt( *console.Menu) {
	 := .Prompt()

	.Primary = func() string {
		// TODO color per connection status (yellow all, green some, grey none)
		return "\x1b[33marpc>\x1b[0m "
	}

	// p.Right = func() string {
	// 	return "\x1b[1;30m" + time.Now().Format("03:04:05.000") + "\x1b[0m"
	// }

	// p.Transient = func() string { return "\x1b[1;30m" + ">> " + "\x1b[0m" }
}

func listCmdFlags( *cobra.Command) []string {
	 := []string{}
	.Flags().VisitAll(func( *pflag.Flag) {
		// skip some
		if .Name == "help" || .Name == "completion" {
			return
		}

		 = append(, "--"+.Name)
	})

	return 
}