/*
** Copyright (C) 2001-2025 Zabbix SIA
**
** This program is free software: you can redistribute it and/or modify it under the terms of
** the GNU Affero General Public License as published by the Free Software Foundation, version 3.
**
** 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 Affero General Public License for more details.
**
** You should have received a copy of the GNU Affero General Public License along with this program.
** If not, see <https://www.gnu.org/licenses/>.
**/

package file

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

// File Types
const (
	zbxFtFile = 1 << iota
	zbxFtDir
	zbxFtSym
	zbxFtSock
	zbxFtBdev
	zbxFtCdev
	zbxFtFifo
	zbxFtAll = (zbxFtFile | zbxFtDir | zbxFtSym | zbxFtSock | zbxFtBdev | zbxFtCdev | zbxFtFifo)
	zbxFtDev = (zbxFtBdev | zbxFtCdev)
)

type fileType uint16

func (f fileType) hasType(t fileType) bool { return f&t != 0 }
func (f *fileType) addType(t fileType)     { *f |= t }

func typesToMask(param string) (fileType, error) {
	var mask fileType = 0
	template := map[string]fileType{
		"file": zbxFtFile,
		"dir":  zbxFtDir,
		"sym":  zbxFtSym,
		"sock": zbxFtSock,
		"bdev": zbxFtBdev,
		"cdev": zbxFtCdev,
		"fifo": zbxFtFifo,
		"all":  zbxFtAll,
		"dev":  zbxFtDev,
	}

	if strings.TrimSpace(param) == "" {
		return 0, nil
	}

	types := strings.Split(param, ",")

	for _, name := range types {
		name = strings.TrimSpace(name)
		if t, ok := template[name]; ok {
			mask.addType(t)
		} else {
			return 0, fmt.Errorf(`Invalid type "%s".`, name)
		}
	}

	return mask, nil
}

// Export -
func (p *Plugin) exportExists(params []string) (result interface{}, err error) {
	var typesIncl fileType
	var typesExcl fileType
	var types fileType
	var f os.FileInfo

	if len(params) > 3 {
		return nil, errors.New("Too many parameters.")
	}
	if len(params) == 0 || params[0] == "" {
		return nil, errors.New("Invalid first parameter.")
	}

	if len(params) > 1 && params[1] != "" {
		if typesIncl, err = typesToMask(params[1]); err != nil {
			return nil, err
		}
	}

	if len(params) > 2 && params[2] != "" {
		if typesExcl, err = typesToMask(params[2]); err != nil {
			return nil, err
		}
	}

	if typesIncl == 0 {
		if typesExcl == 0 {
			typesIncl.addType(zbxFtFile)
		} else {
			typesIncl.addType(zbxFtAll)
		}
	}

	if typesIncl.hasType(zbxFtSym) || typesExcl.hasType(zbxFtSym) {
		if f, err = os.Lstat(params[0]); err == nil {
			if f.Mode()&os.ModeSymlink != 0 {
				types.addType(zbxFtSym)
			}
		} else if !os.IsNotExist(err) {
			return 0, fmt.Errorf("Cannot obtain file information: %s", err)
		}
	}

	if f, err = stdOs.Stat(params[0]); err == nil {
		if f.Mode().IsRegular() {
			types.addType(zbxFtFile)
		} else if f.Mode().IsDir() {
			types.addType(zbxFtDir)
		} else if f.Mode()&os.ModeSocket != 0 {
			types.addType(zbxFtSock)
		} else if f.Mode()&os.ModeCharDevice != 0 {
			types.addType(zbxFtCdev)
		} else if f.Mode()&os.ModeDevice != 0 {
			types.addType(zbxFtBdev)
		} else if f.Mode()&os.ModeNamedPipe != 0 {
			types.addType(zbxFtFifo)
		}
	} else if !os.IsNotExist(err) {
		return 0, fmt.Errorf("Cannot obtain file information: %s", err)
	}

	if !typesExcl.hasType(types) && typesIncl.hasType(types) {
		return 1, nil
	}
	return 0, nil
}
