package controllers

import (
	"encoding/json"
	"errors"
	"time"

	oclib "cloud.o-forge.io/core/oc-lib"
	"cloud.o-forge.io/core/oc-lib/dbs"
	b "cloud.o-forge.io/core/oc-lib/models/booking"
	"cloud.o-forge.io/core/oc-lib/models/workflow_execution"
	beego "github.com/beego/beego/v2/server/web"
)

// Operations about workspace
type BookingController struct {
	beego.Controller
}

// @Title Search
// @Description search booking
// @Param	search		path 	string	true		"the word search you want to get"
// @Success 200 {booking} models.booking
// @router /search/:search [get]
func (o *BookingController) Search() {
	// store and return Id or post with UUID
	search := o.Ctx.Input.Param(":search")
	o.Data["json"] = oclib.Search(nil, search, oclib.LibDataEnum(oclib.BOOKING))
	o.ServeJSON()
}

// @Title GetAll
// @Description find booking by id
// @Success 200 {booking} models.booking
// @router / [get]
func (o *BookingController) GetAll() {
	o.Data["json"] = oclib.LoadAll(oclib.LibDataEnum(oclib.BOOKING))
	o.ServeJSON()
}

// @Title Get
// @Description find booking by id
// @Param	id		path 	string	true		"the id you want to get"
// @Success 200 {booking} models.booking
// @router /:id [get]
func (o *BookingController) Get() {
	id := o.Ctx.Input.Param(":id")
	o.Data["json"] = oclib.LoadOne(oclib.LibDataEnum(oclib.BOOKING), id)
	o.ServeJSON()
}

// @Title Check
// @Description check booking
// @Param	id	path 	string		"id of the datacenter"
// @Param	start_date		path 	string		"the booking start date" format "2006-01-02T15:04:05"
// @Param	end_date		path 	string		"the booking end date" format "2006-01-02T15:04:05"
// @Success 200 {object} models.object
// @router /check/:id/:start_date/:end_date [get]
func (o *BookingController) Check() {
	/*
	 * This function is used to check if a booking is available for a specific datacenter.
	 * It takes the following parameters:
	 * - id: the id of the datacenter
	 * - start_date: the start date of the booking
	 * - end_date: the end date of the booking
	 */
	id := o.Ctx.Input.Param(":id")
	date, err := time.Parse("2006-01-02T15:04:05", o.Ctx.Input.Param(":start_date"))
	date2, err2 := time.Parse("2006-01-02T15:04:05", o.Ctx.Input.Param(":end_date"))
	if err != nil || err2 != nil {
		o.Data["json"] = map[string]interface{}{
			"data": map[string]interface{}{
				"is_available": false,
			},
			"code":  400,
			"error": errors.New("invalid date format"),
		}
	} else {
		booking := &b.Booking{}                                     // create a new booking object
		isAvailable, err2 := booking.CheckBooking(id, date, &date2) // check if the booking is available
		code := 200
		err := ""
		if !isAvailable {
			code = 409
			err = "booking not available"
			if err2 != nil {
				err += " - " + err2.Error()
			}
		}
		o.Data["json"] = map[string]interface{}{
			"data": map[string]interface{}{
				"is_available": isAvailable,
			},
			"code":  code,
			"error": err,
		}
	}
	o.ServeJSON()
}

// @Title Post
// @Description create booking
// @Param	booking		body 	string	true		"the booking you want to post"
// @Success 200 {object} models.object
// @router / [post]
func (o *BookingController) Post() {
	/*
	 * This function is used to create a booking.
	 * It takes the following parameters:
	 * - booking: the booking you want to post
	 * The booking is a JSON object that contains the following fields:
	 * - datacenter_resource_id: the id of the datacenter
	 * - workflow_execution: the workflow execution
	 */
	var resp workflow_execution.WorkflowExecutions
	json.Unmarshal(o.Ctx.Input.CopyBody(10000), &resp)
	dc_id := resp.ResourceID
	// delete all previous bookings
	res := oclib.Search(&dbs.Filters{And: map[string][]dbs.Filter{
		"workflowexecution.workflow_id": {{Operator: dbs.EQUAL.String(), Value: resp.WorkflowID}},
		"datacenter_resource_id":        {{Operator: dbs.EQUAL.String(), Value: dc_id}},
	}}, "", oclib.LibDataEnum(oclib.BOOKING))
	if res.Code != 200 {
		o.Data["json"] = map[string]interface{}{
			"data":  nil,
			"code":  res.Code,
			"error": res.Err,
		}
		o.ServeJSON()
		return
	}
	for _, b := range res.Data { // delete all previous bookings
		oclib.DeleteOne(oclib.LibDataEnum(oclib.BOOKING), b.GetID())
	}
	books := make([]interface{}, 0)
	errormsg := ""
	for _, exec := range resp.Executions { // create new bookings
		if ok, _ := (&b.Booking{}).CheckBooking(dc_id, *exec.ExecDate, exec.EndDate); !ok {
			res.Err += " -> the booking from " + exec.ExecDate.String() + " is already taken."
			o.Data["json"] = map[string]interface{}{
				"data":  nil,
				"code":  res.Code,
				"error": res.Err + " -> the booking from " + exec.ExecDate.String() + " is already taken.",
			}
			o.ServeJSON()
			return
		}
		new := &b.Booking{
			DatacenterResourceID: dc_id,
			WorkflowExecution:    *exec,
		}
		// store the booking
		b := oclib.StoreOne(oclib.LibDataEnum(oclib.BOOKING), new.Serialize())
		if b.Code == 200 {
			books = append(books, b.Data)
		} else {
			errormsg += " -> " + b.Err
		}
	}
	o.Data["json"] = map[string]interface{}{
		"data":  books,
		"code":  200,
		"error": errormsg,
	}
	o.ServeJSON()
}
