//go:build mipsle || riscv64
// +build mipsle riscv64

package database

import (
	"encoding/json"
	"errors"
	"log"
	"os"
	"path/filepath"
	"strings"

	"imuslab.com/zoraxy/mod/database/dbinc"
)

/*
	OpenWRT or RISCV backend

	For OpenWRT or RISCV platform, we will use the filesystem as the database backend
	as boltdb or leveldb is not supported on these platforms, including boltDB and LevelDB
	in conditional compilation will create a build error on these platforms
*/

func newDatabase(dbfile string, backendType dbinc.BackendType) (*Database, error) {
	dbRootPath := filepath.ToSlash(filepath.Clean(dbfile))
	dbRootPath = "fsdb/" + dbRootPath
	err := os.MkdirAll(dbRootPath, 0755)
	if err != nil {
		return nil, err
	}

	log.Println("Filesystem Emulated Key-value Database Service Started: " + dbRootPath)
	return &Database{
		Db:          dbRootPath,
		BackendType: dbinc.BackendFSOnly,
		Backend:     nil,
	}, nil
}

func (d *Database) dump(filename string) ([]string, error) {
	//Get all file objects from root
	rootfiles, err := filepath.Glob(filepath.Join(d.Db.(string), "/*"))
	if err != nil {
		return []string{}, err
	}

	//Filter out the folders
	rootFolders := []string{}
	for _, file := range rootfiles {
		if !isDirectory(file) {
			rootFolders = append(rootFolders, filepath.Base(file))
		}
	}

	return rootFolders, nil
}

func (d *Database) newTable(tableName string) error {

	tablePath := filepath.Join(d.Db.(string), filepath.Base(tableName))
	if !fileExists(tablePath) {
		return os.MkdirAll(tablePath, 0755)
	}
	return nil
}

func (d *Database) tableExists(tableName string) bool {
	tablePath := filepath.Join(d.Db.(string), filepath.Base(tableName))
	if _, err := os.Stat(tablePath); errors.Is(err, os.ErrNotExist) {
		return false
	}

	if !isDirectory(tablePath) {
		return false
	}

	return true
}

func (d *Database) dropTable(tableName string) error {

	tablePath := filepath.Join(d.Db.(string), filepath.Base(tableName))
	if d.tableExists(tableName) {
		return os.RemoveAll(tablePath)
	} else {
		return errors.New("table not exists")
	}

}

func (d *Database) write(tableName string, key string, value interface{}) error {

	tablePath := filepath.Join(d.Db.(string), filepath.Base(tableName))
	js, err := json.Marshal(value)
	if err != nil {
		return err
	}

	key = strings.ReplaceAll(key, "/", "-SLASH_SIGN-")

	return os.WriteFile(filepath.Join(tablePath, key+".entry"), js, 0755)
}

func (d *Database) read(tableName string, key string, assignee interface{}) error {
	if !d.keyExists(tableName, key) {
		return errors.New("key not exists")
	}

	key = strings.ReplaceAll(key, "/", "-SLASH_SIGN-")

	tablePath := filepath.Join(d.Db.(string), filepath.Base(tableName))
	entryPath := filepath.Join(tablePath, key+".entry")
	content, err := os.ReadFile(entryPath)
	if err != nil {
		return err
	}

	err = json.Unmarshal(content, &assignee)
	return err
}

func (d *Database) keyExists(tableName string, key string) bool {
	key = strings.ReplaceAll(key, "/", "-SLASH_SIGN-")
	tablePath := filepath.Join(d.Db.(string), filepath.Base(tableName))
	entryPath := filepath.Join(tablePath, key+".entry")
	return fileExists(entryPath)
}

func (d *Database) delete(tableName string, key string) error {

	if !d.keyExists(tableName, key) {
		return errors.New("key not exists")
	}
	key = strings.ReplaceAll(key, "/", "-SLASH_SIGN-")
	tablePath := filepath.Join(d.Db.(string), filepath.Base(tableName))
	entryPath := filepath.Join(tablePath, key+".entry")

	return os.Remove(entryPath)
}

func (d *Database) listTable(tableName string) ([][][]byte, error) {
	if !d.tableExists(tableName) {
		return [][][]byte{}, errors.New("table not exists")
	}
	tablePath := filepath.Join(d.Db.(string), filepath.Base(tableName))
	entries, err := filepath.Glob(filepath.Join(tablePath, "/*.entry"))
	if err != nil {
		return [][][]byte{}, err
	}

	var results [][][]byte = [][][]byte{}
	for _, entry := range entries {
		if !isDirectory(entry) {
			//Read it
			key := filepath.Base(entry)
			key = strings.TrimSuffix(key, filepath.Ext(key))
			key = strings.ReplaceAll(key, "-SLASH_SIGN-", "/")

			bkey := []byte(key)
			bval := []byte("")
			c, err := os.ReadFile(entry)
			if err != nil {
				break
			}

			bval = c
			results = append(results, [][]byte{bkey, bval})
		}
	}
	return results, nil
}

func (d *Database) close() {
	//Nothing to close as it is file system
}

func isDirectory(path string) bool {
	fileInfo, err := os.Stat(path)
	if err != nil {
		return false
	}

	return fileInfo.IsDir()
}

func fileExists(name string) bool {
	_, err := os.Stat(name)
	if err == nil {
		return true
	}
	if errors.Is(err, os.ErrNotExist) {
		return false
	}
	return false
}
