package nats

import (
	"context"
	"encoding/json"
	"errors"
	cloudevents "github.com/cloudevents/sdk-go/v2"
	"github.com/nats-io/nats.go"
	"golang.source-fellows.com/training-fellow/booking/internal"
	"golang.source-fellows.com/training-fellow/booking/internal/model"
	"log"
	"time"
)

const SubjectNameNewTrainings = "training.instance.new"

type Config struct {
	ServerUrl string `envconfig:"NATS_SERVER_URL" required:"true"`
}

type TrainingListener struct {
	config        Config
	trainingStore internal.TrainingStore
	listenRootCtx context.Context
}

func NewTrainingListener(config Config, trainingStore internal.TrainingStore) *TrainingListener {
	return &TrainingListener{trainingStore: trainingStore, config: config}
}

func (tl *TrainingListener) StateChanged(service, state string) {

}

func (tl *TrainingListener) ListenForNewTrainings(ctx context.Context) error {

	if tl.listenRootCtx != nil {
		return errors.New("listen already called")
	}
	tl.listenRootCtx = ctx

	nc, err := nats.Connect(tl.config.ServerUrl)
	if err != nil {
		return err
	}

	// Responding to a request message
	subscription, err := nc.Subscribe(SubjectNameNewTrainings, tl.HandleMessage)

	if err != nil {
		return err
	}
	<-ctx.Done()
	log.Println("closing subscription")
	subscription.Unsubscribe()
	nc.Close()
	return nil
}

func (tl *TrainingListener) HandleMessage(msg *nats.Msg) {

	innerCtx, cancel := context.WithTimeout(tl.listenRootCtx, 3*time.Second)
	defer cancel()

	/*
		err := msg.Respond([]byte("ok"))
		if err != nil {
			log.Println("1", err)
			return
		}
	*/

	log.Println("new training")
	event := cloudevents.NewEvent()
	err := json.Unmarshal(msg.Data, &event)
	if err != nil {
		log.Println(err)
		return
	}

	training := Training{}
	err = json.Unmarshal(event.Data(), &training)
	if err != nil {
		log.Println(err)
		return
	}

	err = tl.trainingStore.AddNewTraining(innerCtx, mapMessageToTraining(training))
	if err != nil {
		log.Println(err)
		return
	}
}

func mapMessageToTraining(msg Training) *model.Training {
	return model.NewTrainingWithNumberOfDays(msg.Name, msg.NumberOfDays)
}

type Training struct {
	Name         string
	NumberOfDays int
}
