package main

import (
	"bufio"
	"flag"
	"fmt"
	"os"
	"strings"

	"g.arns.lt/zordsdavini/zordfsdb"
)

var (
	rootDir        string
	executeCommand string
)

func main() {
	flag.StringVar(&rootDir, "d", "./", "database root directory")
	flag.StringVar(&executeCommand, "e", "", "execute command")

	flag.Parse()

	db, err := zordfsdb.InitDB(rootDir)
	if err != nil {
		panic(err)
	}

	if len(executeCommand) > 0 {
		ExecuteCommand(db, executeCommand)
	} else {
		reader := bufio.NewReader(os.Stdin)
		fmt.Println("ZordFsDB Shell")
		fmt.Println("---------------------")
		fmt.Println("DB initialized at ", rootDir)
		fmt.Println("")
		fmt.Println("Copyright (c) 2024 zordsdavini@arns.lt under MIT license")
		fmt.Println("")
		fmt.Println("Type HELP to get list of commands or QUIT to get out")
		fmt.Println("")

		for {
			fmt.Print("zordfsdb> ")
			text, _ := reader.ReadString('\n')
			// convert CRLF to LF
			text = strings.Replace(text, "\n", "", -1)
			fmt.Println(text)

			if strings.Compare("QUIT", strings.ToUpper(text)) == 0 || strings.Compare("\\Q", strings.ToUpper(text)) == 0 {
				fmt.Println("\nBye o/")
				break
			}

			if strings.Compare("HELP", strings.ToUpper(text)) == 0 || strings.Compare("\\H", strings.ToUpper(text)) == 0 {
				fmt.Println(`
ZordFsDB shell supported commands:

KEYS [path] - get list of existing properties/objects/lists
GET [path] - get value of property
INC [path] - increase abcex value in property
DEC [path] - decrease abcex value in property
NOW [path] - set value to datetime string in property
DEL [path] - remove property/object/list (no confirmation)
SAVE [path] [value] - set value to property
CREATE [path] - create list/object in path (parent path should exist)
ADD [path] - add abcex indexed object to list. Will return index
HELP - will print this help
QUIT - exit the shell

Last used path can be accessed by _
Last returned index (after ADD command) can be accessed by $

Short commands can be as:
KEYS(\K), GET(\G), INC(\I), DEC(\D), NOW(\N), DEL(\R), SAVE(\S), CREATE(\C), ADD(\A), HELP(\H), QUIT(\Q)
                    `)
				continue
			}

			ExecuteCommand(db, text)
		}
	}
}

func ExecuteCommand(db zordfsdb.DB, commands string) error {
	for _, command := range strings.Split(commands, ";") {
		command = strings.Trim(command, " ")
		parts := strings.Split(command, " ")

		switch strings.ToUpper(parts[0]) {
		case "KEYS":
		case "\\K":
			kpath := "."
			if !(len(parts) == 1 || len(parts[1]) == 0) {
				kpath = parts[1]
			}

			fmt.Printf("{%s} KEYS: ", kpath)
			fmt.Println(db.Keys(kpath))
			fmt.Println("")
			break

		case "GET":
		case "\\G":
			if len(parts) == 1 || len(parts[1]) == 0 {
				return fmt.Errorf("err format: GET [path]\n")
			}

			value, ok := db.Get(parts[1])
			if !ok {
				fmt.Println("No value found.")
				fmt.Println("")
			} else {
				fmt.Printf("{%s} VALUE: ", parts[1])
				fmt.Println(value)
				fmt.Println("")
			}
			break

		case "INC":
		case "\\I":
			if len(parts) == 1 || len(parts[1]) == 0 {
				return fmt.Errorf("err format: INC [path]\n")
			}

			ok := db.Inc(parts[1])
			if !ok {
				fmt.Println("No value found.")
				fmt.Println("")
			} else {
				fmt.Printf("{%s} INC OK\n", parts[1])
				fmt.Println("")
			}
			break

		case "DEC":
		case "\\D":
			if len(parts) == 1 || len(parts[1]) == 0 {
				return fmt.Errorf("err format: DEC [path]\n")
			}

			ok := db.Dec(parts[1])
			if !ok {
				fmt.Println("No value found.")
				fmt.Println("")
			} else {
				fmt.Printf("{%s} DEC OK\n", parts[1])
				fmt.Println("")
			}
			break

		case "NOW":
		case "\\N":
			if len(parts) == 1 || len(parts[1]) == 0 {
				return fmt.Errorf("err format: NOW [path]\n")
			}

			ok := db.Now(parts[1])
			if !ok {
				fmt.Println("No value found.")
				fmt.Println("")
			} else {
				fmt.Printf("{%s} NOW OK\n", parts[1])
				fmt.Println("")
			}
			break

		case "DEL":
		case "\\R":
			if len(parts) == 1 || len(parts[1]) == 0 {
				return fmt.Errorf("err format: DEL [path]\n")
			}

			ok := db.Del(parts[1])
			if !ok {
				fmt.Println("No value/object found.")
				fmt.Println("")
			} else {
				fmt.Printf("{%s} DELETED\n", parts[1])
				fmt.Println("")
			}
			break

		case "SAVE":
		case "\\S":
			if len(parts) < 3 || len(parts[1]) == 0 {
				return fmt.Errorf("err format: SAVE [path] [value|long value]\n")
			}

			value := strings.Trim(strings.Join(parts[2:], " "), "\"")

			ok := db.Save(parts[1], value)
			if !ok {
				fmt.Println("No value found.")
				fmt.Println("")
			} else {
				fmt.Printf("{%s} SAVED\n", parts[1])
				fmt.Println("")
			}
			db.Refresh()
			break

		case "CREATE":
		case "\\C":
			if len(parts) == 1 || len(parts[1]) == 0 {
				return fmt.Errorf("err format: CREATE [path]\n")
			}

			ok := db.CreateNode(parts[1])
			if !ok {
				fmt.Println("No path found.")
				fmt.Println("")
			} else {
				fmt.Printf("{%s} CREATED\n", parts[1])
				fmt.Println("")
			}
			break

		case "ADD":
		case "\\A":
			if len(parts) == 1 || len(parts[1]) == 0 {
				return fmt.Errorf("err format: ADD [path]\n")
			}

			id, err := db.AddObject(parts[1])
			if err != nil {
				fmt.Println("No path found.")
				fmt.Println("")
			} else {
				fmt.Printf("{%s} ADDED with ID: %s\n", parts[1], id)
				fmt.Println("")
			}
			break

		default:
			fmt.Println("err[00] unknown command, try HELP")

		}
	}

	return nil
}