package grouptask

import (
	"encoding/json"
	"jannex/admin-dashboard-backend/modules/cache"
	"jannex/admin-dashboard-backend/modules/grouptasks"
	"jannex/admin-dashboard-backend/modules/structs"
	"jannex/admin-dashboard-backend/modules/utils"
	"jannex/admin-dashboard-backend/socketclients"

	"github.com/gofiber/fiber/v2"
	"github.com/rs/zerolog/log"
)

func StartGroupTask(c *fiber.Ctx) error {
	// swagger:operation POST /grouptasks/start grouptasks grouptasksStart
	// ---
	// summary: Start a new group task
	// consumes:
	// - application/json
	// produces:
	// - application/json
	// parameters:
	// - name: X-Api-Key
	//   in: header
	//   description: You can create a new api key in your user profile
	// - name: body
	//   in: body
	//   description: JSON -> Category str, GroupId str, Description str, GlobalInputs [{key, value}, {...}]
	// responses:
	//   '200':
	//     description: New group task created successfully
	//   '400':
	//     description: Invalid request body
	//   '401':
	//     description: No permissions
	//   '422':
	//     description: No group tasks in this category or provided global inputs incomplete

	var body structs.ApiGroupTaskRequest

	if err := c.BodyParser(&body); err != nil {
		log.Error().Msg("Failed to parse body, err: " + err.Error())
		return c.Status(fiber.StatusBadRequest).JSON(err)
	}

	if errValidation := utils.ValidateStruct(body); errValidation != nil {
		log.Error().Msgf("Failed to validate body, err: %v", errValidation)
		return c.Status(fiber.StatusBadRequest).JSON(errValidation)
	}

	userId := c.Locals("userId").(string)

	if !socketclients.HasPermission(userId, utils.PermissionUserProfileApiKeys) || !socketclients.HasXYPermission(userId, utils.PermissionGroupTasksOverviewXYNewTask, body.Category) {
		return c.SendStatus(fiber.StatusUnauthorized)
	}

	categoryGroup := cache.GetCategoryGroupByCategory(body.Category)

	if categoryGroup.Category == "" {
		return c.Status(fiber.StatusBadRequest).JSONP(fiber.Map{"err": "Category not found"})
	}

	group := getGroupFromCategoryGroup(categoryGroup, body.GroupId)

	if group.Category == "" {
		return c.Status(fiber.StatusBadRequest).JSONP(fiber.Map{"err": "GroupId not found"})
	}

	var bodyGlobalInputs []map[string]string

	err := json.Unmarshal(body.GlobalInputs, &bodyGlobalInputs)

	if err != nil {
		log.Error().Msgf("Failed to unmarshal body global inputs: %v", err)
		return c.SendStatus(fiber.StatusBadRequest)
	}

	if len(group.Tasks) == 0 || !globalInputsComplete(bodyGlobalInputs, group.GlobalInputs) {
		return c.SendStatus(fiber.StatusUnprocessableEntity)
	}

	grouptasks.StartGroupTask(userId, structs.GroupTasks{
		Category:      body.Category,
		GroupId:       body.GroupId,
		GroupName:     group.Name,
		Description:   body.Description,
		NumberOfSteps: uint8(len(group.Tasks)),
		GlobalInputs:  convertJsonGlobalInputToStruct(bodyGlobalInputs, group.GlobalInputs),
	})
	return c.SendStatus(fiber.StatusOK)
}

func getGroupFromCategoryGroup(categoryGroup structs.CategoryGroup, groupId string) structs.Group {
	for _, group := range categoryGroup.Groups {
		if group.Id == groupId {
			return group
		}
	}

	return structs.Group{}
}

func globalInputsComplete(bodyGlobalInputs []map[string]string, groupGlobalInputs []structs.GlobalInputs) bool {
	for _, gGlobalInput := range groupGlobalInputs {
		if !isInList(gGlobalInput.ParameterName, bodyGlobalInputs) {
			return false
		}
	}

	return true
}

func isInList(gParameterName string, bodyInputs []map[string]string) bool {
	for _, bInput := range bodyInputs {
		if bInput[gParameterName] != "" {
			return true
		}
	}

	return false
}

type GlobalInputs struct {
	ParameterName string `json:"parameterName"`
	Value         string `json:"value"`
}

func convertJsonGlobalInputToStruct(bodyGlobalInputs []map[string]string, groupGlobalInputs []structs.GlobalInputs) string {
	var globalInputs []GlobalInputs

	for _, gGlobalInput := range groupGlobalInputs {
		for _, bInput := range bodyGlobalInputs {
			if bInput[gGlobalInput.ParameterName] != "" {
				globalInputs = append(globalInputs, GlobalInputs{
					ParameterName: gGlobalInput.ParameterName,
					Value:         bInput[gGlobalInput.ParameterName],
				})
			}
		}
	}

	return utils.MarshalJson(globalInputs)
}
