// Package notify provides utilities to the notify packages
package notify

import (
	"bytes"
	"fmt"
	"io"
	"log/slog"
	"net/http"
	"sync"
	"time"

	"quantex.com/qfixdpl/src/common/logger"
	"quantex.com/qfixdpl/src/common/tracerr"
	"quantex.com/qfixdpl/src/domain"
)

// maxRetry is the send message maximum number of tries.
const maxRetry = 5

// waitBetweenTries is the time in seconds that the SendCo func should wait before try again.
const waitBetweenTries = 5

//revive:disable:argument-limit // need this length to pass the logger
func SendWithRetry(text string, status domain.MessageStatus, waitgroup *sync.WaitGroup, url string,
	getMessage func(string, domain.MessageStatus) string,
) {
	loc, err := time.LoadLocation("America/Argentina/Buenos_Aires")
	if err != nil {
		logger.ErrorWoNotifier("error loading timezone %v", err)

		return
	}

	// TODO check why we have these, here it should be a better way to avoid this
	start := time.Date(time.Now().Year(), time.Now().Month(), time.Now().Day(), 7, 30, 0, 0, loc)

	if time.Now().Before(start) {
		slog.Info("skipping notification, inactive app")

		return
	}

	for range maxRetry {
		err = send(text, status, url, getMessage)
		if err == nil {
			slog.Info("msg sent with notifier: " + text)

			if waitgroup != nil {
				waitgroup.Done()
			}

			return
		}

		time.Sleep(time.Second * waitBetweenTries)
	}

	logger.ErrorWoNotifier("unable to send msg in %d tries", maxRetry)

	if waitgroup != nil {
		waitgroup.Done()
	}
}

//revive:enable:argument-limit

func send(text string, status domain.MessageStatus, url string,
	getMessage func(string, domain.MessageStatus) string,
) (errOut error) {
	slog.Debug("URL:> " + url)

	msg := getMessage(text, status) // this function changes

	slog.Debug(msg)

	jsonStr := []byte(msg)

	req, err := http.NewRequest(http.MethodPost, url, bytes.NewBuffer(jsonStr)) //nolint:noctx // we don't need context here
	if err != nil {
		logger.ErrorWoNotifier("%v", err.Error())

		return err //nolint:wrapcheck // we don't need to wrap this error
	}

	req.Header.Set("Content-Type", "application/json")

	client := &http.Client{
		Timeout: time.Second * 10,
	}

	resp, err := client.Do(req)
	if err != nil {
		return err //nolint:wrapcheck // we don't need to wrap this error
	}

	if resp == nil {
		err = tracerr.Errorf("error at send: '%s' response is nil", req.URL)

		logger.ErrorWoNotifier("%v", err.Error())

		return err
	}

	defer func() {
		if err := resp.Body.Close(); err != nil {
			err = tracerr.Errorf("error closing body request while sending msg at send: %v", err)

			logger.ErrorWoNotifier("%v", err.Error())
		}
	}()

	r := resp.Status
	slog.Debug("response Status: " + r)
	slog.Debug(fmt.Sprintf("response Headers: %+v", resp.Header))

	body, err := io.ReadAll(resp.Body)
	if err != nil {
		logger.ErrorWoNotifier("error reading response body while sending msg, error: %s", err.Error())
	}

	slog.Debug("response Body: " + string(body))

	if resp.StatusCode != http.StatusOK {
		err = tracerr.Errorf("error sending msg statusCode: %d body: %s", resp.StatusCode, string(body))

		logger.ErrorWoNotifier("%v", err.Error())

		return err
	}

	return nil
}

func ValidateChannelConfig(channels domain.Channels) error {
	if channels.Test == "" || channels.Web == "" || channels.Panic == "" || channels.Error == "" {
		return tracerr.Errorf("channels configuration is needed: Test, Web, Panic and/or Error are empty. "+
			"cfg: %v", channels)
	}

	return nil
}
