package main

import (
	// External
	werr "git.gibonuddevalla.se/go/wrappederror"

	// Internal
	"smon/notification"

	// Standard
	"database/sql"
	"encoding/json"
	"time"
)

var (
	nodataAreaID    int
	nodataSectionID int
)

// nodataLoop  checks if datapoint last_value is larger than the nodata_problem_seconds period and
// marks them as problems. They are then notified.
func nodataLoop() {
	var datapoints []Datapoint
	var err error

	// TODO - should be configurable
	ticker := time.NewTicker(time.Second * time.Duration(smonConf.NodataInterval))
	for {
		<-ticker.C
		datapoints, err = nodataDatapoints()
		if err != nil {
			err = werr.Wrap(err).Log()
			logger.Error("nodata", "error", err)
			continue
		}

		if len(datapoints) == 0 {
			continue
		}

		for _, datapoint := range datapoints {
			nodataNotify(datapoint, "[NODATA] problem")
		}

		logger.Info("nodata", "problem_ids", datapoints)
	}
}

func nodataNotify(datapoint Datapoint, state string) (err error) {
	err = notificationManager.Send(-1, []byte(state+": "+datapoint.Name), func(notificationService *notification.Service, err error) {
		logger.Info(
			"notification",
			"service", (*notificationService).GetType(),
			"datapointID", datapoint.ID,
			"prio", (*notificationService).GetPrio(),
			"ok", true,
		)

		var errBody any
		if err != nil {
			errBody, _ = json.Marshal(err)
		} else {
			errBody = nil
		}
		_, err = service.Db.Conn.Exec(
			`
			INSERT INTO notification_send(notification_id, datapoint_nodata_id, uuid, ok, error)
			SELECT
				id, $3, '', $4, $5
			FROM notification
			WHERE
				service=$1 AND
				prio=$2
					`,
			(*notificationService).GetType(),
			(*notificationService).GetPrio(),
			datapoint.ID,
			err == nil,
			errBody,
		)
		if err != nil {
			err = werr.Wrap(err).Log()
			logger.Error("entry", "error", err)
		}
	})
	if err != nil {
		err = werr.Wrap(err).Log()
		logger.Error("notification", "error", err)
	}
	return
}

func nodataDatapoints() (datapoints []Datapoint, err error) {
	datapoints = []Datapoint{}

	var rows *sql.Rows
	rows, err = service.Db.Conn.Query(`
		UPDATE datapoint
		SET
			nodata_is_problem = true
		FROM (
			SELECT
				id
			FROM
				datapoint
			WHERE
				nodata_problem_seconds > 0 AND
				NOT nodata_is_problem AND
				extract(EPOCH from (NOW() - last_value))::int > nodata_problem_seconds
			) AS subquery
		WHERE
			datapoint.id = subquery.id
		RETURNING
			datapoint.id,
			datapoint.name

	`)
	if err != nil {
		err = werr.Wrap(err)
		return
	}
	defer rows.Close()

	var dp Datapoint
	for rows.Next() {
		if err = rows.Scan(&dp.ID, &dp.Name); err != nil {
			err = werr.Wrap(err)
			return
		}
		datapoints = append(datapoints, dp)
	}
	return
}
