package fzf

import (
	"errors"
	"os"
	"strings"
)

// History struct represents input history
type History struct {
	path     string
	lines    []string
	modified map[int]string
	maxSize  int
	cursor   int
}

// NewHistory returns the pointer to a new History struct
func NewHistory(path string, maxSize int) (*History, error) {
	fmtError := func(e error) error {
		if os.IsPermission(e) {
			return errors.New("permission denied: " + path)
		}
		return errors.New("invalid history file: " + e.Error())
	}

	// Read history file
	data, err := os.ReadFile(path)
	if err != nil {
		// If it doesn't exist, check if we can create a file with the name
		if os.IsNotExist(err) {
			data = []byte{}
			if err := os.WriteFile(path, data, 0600); err != nil {
				return nil, fmtError(err)
			}
		} else {
			return nil, fmtError(err)
		}
	}
	// Split lines and limit the maximum number of lines
	lines := strings.Split(strings.Trim(string(data), "\n"), "\n")
	if len(lines[len(lines)-1]) > 0 {
		lines = append(lines, "")
	}
	return &History{
		path:     path,
		maxSize:  maxSize,
		lines:    lines,
		modified: make(map[int]string),
		cursor:   len(lines) - 1}, nil
}

func (h *History) append(line string) error {
	// We don't append empty lines
	if len(line) == 0 {
		return nil
	}

	lines := append(h.lines[:len(h.lines)-1], line)
	if len(lines) > h.maxSize {
		lines = lines[len(lines)-h.maxSize:]
	}
	h.lines = append(lines, "")
	return os.WriteFile(h.path, []byte(strings.Join(h.lines, "\n")), 0600)
}

func (h *History) override(str string) {
	// You can update the history, but they're not written to the file
	if h.cursor == len(h.lines)-1 {
		h.lines[h.cursor] = str
	} else if h.cursor < len(h.lines)-1 {
		h.modified[h.cursor] = str
	}
}

func (h *History) current() string {
	if str, prs := h.modified[h.cursor]; prs {
		return str
	}
	return h.lines[h.cursor]
}

func (h *History) previous() string {
	if h.cursor > 0 {
		h.cursor--
	}
	return h.current()
}

func (h *History) next() string {
	if h.cursor < len(h.lines)-1 {
		h.cursor++
	}
	return h.current()
}
