/*
** 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 main

import (
	"encoding/json"
	"flag"
	"fmt"
	"io/ioutil"
	"os"
	"strconv"
	"time"

	"golang.zabbix.com/agent2/pkg/zbxcomms"
	"golang.zabbix.com/sdk/conf"
	"golang.zabbix.com/sdk/log"
)

type MockServerOptions struct {
	LogType          string `conf:"default=console"`
	LogFile          string `conf:"optional"`
	DebugLevel       int    `conf:"range=0:5,default=3"`
	Port             int    `conf:"range=1:65535,default=10051"`
	Timeout          int    `conf:"range=1:30,default=5"`
	ActiveChecksFile string `conf:"optional"`
}

var options MockServerOptions

func handleConnection(c *zbxcomms.Connection) {
	defer c.Close()

	js, err := c.Read()
	if err != nil {
		log.Warningf("Read failed: %s\n", err)
		return
	}

	log.Debugf("got '%s'", string(js))

	var pairs map[string]interface{}
	if err := json.Unmarshal(js, &pairs); err != nil {
		log.Warningf("Unmarshal failed: %s\n", err)
		return
	}

	switch pairs["request"] {
	case "active checks":
		activeChecks, err := ioutil.ReadFile(options.ActiveChecksFile)
		if err == nil {
			err = c.Write(activeChecks)
		}
		if err != nil {
			log.Warningf("Write failed: %s\n", err)
			return
		}
	case "agent data":
		err = c.WriteString("{\"response\":\"success\",\"info\":\"processed: 0; failed: 0; total: 0; seconds spent: 0.042523\"}")
		if err != nil {
			log.Warningf("Write failed: %s\n", err)
			return
		}

	default:
		log.Warningf("Unsupported request: %s\n", pairs["request"])
		return
	}

}

func main() {
	var confFlag string
	const (
		confDefault     = "mock_server.conf"
		confDescription = "Path to the configuration file"
	)
	flag.StringVar(&confFlag, "config", confDefault, confDescription)
	flag.StringVar(&confFlag, "c", confDefault, confDescription+" (shorhand)")

	var foregroundFlag bool
	const (
		foregroundDefault     = true
		foregroundDescription = "Run Zabbix mock server in foreground"
	)
	flag.BoolVar(&foregroundFlag, "foreground", foregroundDefault, foregroundDescription)
	flag.BoolVar(&foregroundFlag, "f", foregroundDefault, foregroundDescription+" (shorhand)")
	flag.Parse()

	if err := conf.Load(confFlag, &options); err != nil {
		fmt.Fprintf(os.Stderr, "%s\n", err.Error())
		os.Exit(1)
	}

	var logType, logLevel int
	switch options.LogType {
	case "console":
		logType = log.Console
	case "file":
		logType = log.File
	}
	switch options.DebugLevel {
	case 0:
		logLevel = log.Info
	case 1:
		logLevel = log.Crit
	case 2:
		logLevel = log.Err
	case 3:
		logLevel = log.Warning
	case 4:
		logLevel = log.Debug
	case 5:
		logLevel = log.Trace
	}

	if err := log.Open(logType, logLevel, options.LogFile, 0); err != nil {
		fmt.Fprintf(os.Stderr, "Cannot initialize logger: %s\n", err.Error())
		os.Exit(1)
	}

	greeting := fmt.Sprintf("Starting Zabbix Mock server [(hostname placeholder)]. (version placeholder)")
	log.Infof(greeting)

	if foregroundFlag {
		if options.LogType != "console" {
			fmt.Println(greeting)
		}
		fmt.Println("Press Ctrl+C to exit.")
	}

	log.Infof("using configuration file: %s", confFlag)

	listener, err := zbxcomms.Listen(":" + strconv.Itoa(options.Port))
	if err != nil {
		log.Critf("Listen failed: %s\n", err)
		return
	}
	defer listener.Close()

	for {
		c, err := listener.Accept(time.Second*time.Duration(options.Timeout), zbxcomms.TimeoutModeShift)
		if err != nil {
			log.Critf("Accept failed: %s\n", err)
			return
		}
		go handleConnection(c)
	}
}
