/*
Copyright © 2024-2025 Thomas von Dein

This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package app

import (
	"fmt"
	"io"
	"os"
	"strings"
	"unicode/utf8"
)

type DbAttr struct {
	Key       string
	Preview   string
	Val       []byte
	Data      string // alias
	Args      []string
	Tags      []string
	File      string
	Encrypted bool
	Binary    bool

	// conf flags, needed for incoming rest requests
	Fulltext bool
}

// check if value  is to be read  from a file or  stdin, setup preview
// text according to flags, lowercase key
func (attr *DbAttr) ParseKV() error {
	attr.Key = strings.ToLower(attr.Args[0])

	switch len(attr.Args) {
	case 1:
		// 1 arg = key + read from file or stdin
		if attr.File == "" {
			attr.File = "-"
		}
	case 2:
		attr.Val = []byte(attr.Args[1])

		if attr.Args[1] == "-" {
			attr.File = "-"
		}
	}

	if attr.File != "" {
		if err := attr.GetFileValue(); err != nil {
			return err
		}
	}

	attr.SetPreview()

	return nil
}

func (attr *DbAttr) SetPreview() {
	switch {
	case attr.Binary:
		attr.Preview = "<binary-content>"
	case attr.Encrypted:
		attr.Preview = "<encrypted-content>"
	default:
		if len(attr.Val) > MaxValueWidth {
			attr.Preview = string(attr.Val)[0:MaxValueWidth] + "..."

			if strings.Contains(attr.Preview, "\n") {
				parts := strings.Split(attr.Preview, "\n")
				if len(parts) > 0 {
					attr.Preview = parts[0]
				}
			}
		} else {
			attr.Preview = string(attr.Val)
		}
	}
}

func (attr *DbAttr) GetFileValue() error {
	var fd io.Reader

	if attr.File == "-" {
		stat, _ := os.Stdin.Stat()
		if (stat.Mode() & os.ModeCharDevice) == 0 {
			fd = os.Stdin
		}
	} else {
		filehandle, err := os.OpenFile(attr.File, os.O_RDONLY, 0600)
		if err != nil {
			return fmt.Errorf("failed to open file %s: %w", attr.File, err)
		}

		fd = filehandle
	}

	if fd != nil {
		// read from file or stdin pipe
		data, err := io.ReadAll(fd)
		if err != nil {
			return fmt.Errorf("failed to read from pipe: %w", err)
		}

		// poor man's text file test
		attr.Val = data

		if utf8.ValidString(string(data)) {
			attr.Binary = false
		} else {
			attr.Binary = true
		}
	} else {
		// read from console stdin
		var input string
		var data string

		for {
			_, err := fmt.Scanln(&input)
			if err != nil {
				break
			}
			data += input + "\n"
		}

		attr.Val = []byte(data)
	}

	return nil
}
