package booking

import (
	"encoding/json"
	"time"

	"cloud.o-forge.io/core/oc-lib/dbs"
	"cloud.o-forge.io/core/oc-lib/models/utils"
	"cloud.o-forge.io/core/oc-lib/models/workflow_execution"
	"cloud.o-forge.io/core/oc-lib/tools"
	"github.com/google/uuid"
	"go.mongodb.org/mongo-driver/bson/primitive"
)

/*
* Booking is a struct that represents a booking
 */
type Booking struct {
	workflow_execution.WorkflowExecution        // WorkflowExecution contains the workflow execution data
	DatacenterResourceID                 string `json:"datacenter_resource_id,omitempty" bson:"datacenter_resource_id,omitempty" validate:"required"` // DatacenterResourceID is the ID of the datacenter resource specified in the booking
}

// CheckBooking checks if a booking is possible on a specific datacenter resource
func (wfa *Booking) CheckBooking(id string, start time.Time, end *time.Time) (bool, error) {
	// check if
	if end == nil {
		// if no end... then Book like a savage
		return true, nil
	}
	e := *end
	accessor := wfa.GetAccessor(nil)
	res, code, err := accessor.Search(&dbs.Filters{
		And: map[string][]dbs.Filter{ // check if there is a booking on the same datacenter resource by filtering on the datacenter_resource_id, the state and the execution date
			"datacenter_resource_id":  {{Operator: dbs.EQUAL.String(), Value: id}},
			"workflowexecution.state": {{Operator: dbs.EQUAL.String(), Value: workflow_execution.SCHEDULED.EnumIndex()}},
			"workflowexecution.execution_date": {
				{Operator: dbs.LTE.String(), Value: primitive.NewDateTimeFromTime(e)},
				{Operator: dbs.GTE.String(), Value: primitive.NewDateTimeFromTime(start)},
			},
		},
	}, "")
	if code != 200 {
		return false, err
	}
	return len(res) == 0, nil
}

// tool to convert the argo status to a state
func (wfa *Booking) ArgoStatusToState(status string) *Booking {
	wfa.WorkflowExecution.ArgoStatusToState(status)
	return wfa
}

func (ao *Booking) GetID() string {
	return ao.UUID
}

func (r *Booking) GenerateID() {
	r.UUID = uuid.New().String()
}

func (d *Booking) GetName() string {
	return d.UUID + "_" + d.ExecDate.String()
}

func (d *Booking) GetAccessor(caller *tools.HTTPCaller) utils.Accessor {
	data := New()                    // Create a new instance of the accessor
	data.Init(tools.BOOKING, caller) // Initialize the accessor with the BOOKING model type
	return data
}

func (dma *Booking) Deserialize(j map[string]interface{}) utils.DBObject {
	b, err := json.Marshal(j)
	if err != nil {
		return nil
	}
	json.Unmarshal(b, dma)
	return dma
}

func (dma *Booking) Serialize() map[string]interface{} {
	var m map[string]interface{}
	b, err := json.Marshal(dma)
	if err != nil {
		return nil
	}
	json.Unmarshal(b, &m)
	return m
}
