package robot

import (
	"jannex/robot-control-manager/modules/cache"
	"jannex/robot-control-manager/modules/database"
	"jannex/robot-control-manager/modules/logger"
	"jannex/robot-control-manager/modules/robot"
	"jannex/robot-control-manager/modules/structs"
	"jannex/robot-control-manager/modules/utils"
	"time"

	"git.ex.umbach.dev/Alex/roese-utils/rspagination"
	"git.ex.umbach.dev/Alex/roese-utils/rsutils"
	"github.com/gofiber/fiber/v2"
	"github.com/google/uuid"
)

func FirstRequest(c *fiber.Ctx) error {
	// swagger:operation POST /robot robot robotFirstRequest
	// ---
	// summary: First request from robot.
	// description: |
	//   This is the first request from the robot. It will be used to identify the robot.
	// consumes:
	// - application/json
	// produces:
	// - application/json
	// parameters:
	// - in: body
	//   name: body
	//   description: First request body.
	//   required: true
	//   schema:
	//     "$ref": "#/definitions/FirstRequestBody"
	// responses:
	//   "200":
	//    description: Robot identified
	//    schema:
	//     "$ref": "#/definitions/StatusResponse"
	//   "400":
	//    description: Invalid request body
	//   "403":
	//    description: Permit join is enabled

	var body structs.FirstRequestBody

	if err := rsutils.BodyParserHelper(c, &body); err != nil {
		return c.SendStatus(fiber.StatusBadRequest)
	}

	var foundRobot structs.Robot

	database.DB.First(&foundRobot, "id = ?", body.Id)

	if !cache.IsPermitJoinEnabled() && foundRobot.Id == "" {
		logger.AddSystemLog("Unauthorized robot tried to connect with id %v and type %v", body.Id, utils.GetRobotTypeString(body.Type))
		return c.SendStatus(fiber.StatusForbidden)
	}

	if foundRobot.Id == "" {
		now := time.Now()

		newUnauthorizedRobot := structs.UnauthorizedRobot{
			Id:              body.Id,
			Type:            body.Type,
			Address:         c.IP(),
			FirmwareVersion: body.FirmwareVersion,
			ConnectedAt:     now,
			CreatedAt:       now,
		}

		cache.AddUnauthorizedRobot(&newUnauthorizedRobot)

		totalPages := cache.GetAllUnauthorizedRobots(rspagination.PageQuery{Page: 1}).TotalPages

		robot.BroadcastSSEMessage(structs.SSEMessage{
			Cmd: utils.SSESentCmdAddUnauthorizedRobot,
			Body: struct {
				UnauthorizedRobot *structs.UnauthorizedRobot
				TotalPages        int
			}{
				UnauthorizedRobot: &newUnauthorizedRobot,
				TotalPages:        totalPages,
			},
		})

		logger.AddSystemLog("Unauthorized robot connected with id %v and type %v", body.Id, utils.GetRobotTypeString(body.Type))
	} else {
		newRobot := structs.Robot{
			Id:              body.Id,
			Type:            body.Type,
			Name:            foundRobot.Name,
			Status:          utils.RobotStatusIdle,
			Address:         c.IP(),
			CurrentJobId:    "",
			FirmwareVersion: body.FirmwareVersion,
			ConnectedAt:     time.Now(),
			CreatedAt:       foundRobot.CreatedAt,
		}

		cache.AddRobot(&newRobot)

		database.DB.Model(&structs.Robot{}).
			Where("id = ?", newRobot.Id).
			Updates(structs.Robot{
				Status:          newRobot.Status,
				Type:            newRobot.Type,
				Address:         newRobot.Address,
				FirmwareVersion: newRobot.FirmwareVersion,
			})

		addRobotSSEMessage(&newRobot)

		logger.AddSystemLog("Robot connected with id %v and type %v", body.Id, utils.GetRobotTypeString(body.Type))
	}

	return c.JSON(structs.StatusResponse{Status: utils.ResponseStatusOk})
}

func addRobotSSEMessage(newRobot *structs.Robot) {
	totalPages := cache.GetAllRobots(rspagination.PageQuery{Page: 1}).TotalPages

	robot.BroadcastSSEMessage(structs.SSEMessage{
		Cmd: utils.SSESentCmdAddRobot,
		Body: struct {
			Robot                        *structs.Robot
			TotalPages                   int
			UnauthorizedRobotsTotalPages int
		}{
			Robot:                        newRobot,
			TotalPages:                   totalPages,
			UnauthorizedRobotsTotalPages: cache.GetAllUnauthorizedRobots(rspagination.PageQuery{Page: 1}).TotalPages,
		},
	})
}

func AuthorizeRobot(c *fiber.Ctx) error {
	// swagger:operation POST /robot/authorize/{robotId} robot robotAuthorize
	// ---
	// summary: Authorize robot.
	// consumes:
	// - application/json
	// produces:
	// - application/json
	// parameters:
	// - in: params
	//   name: robotId
	//   description: Robot id.
	//   required: true
	//   schema:
	//     type: string
	// responses:
	//   "200":
	//    description: Robot authorized
	//   "400":
	//    description: Invalid robot id
	//   "422":
	//    description: Robot not found

	var params structs.RobotIdParams

	if err := rsutils.ParamsParserHelper(c, &params); err != nil {
		return c.SendStatus(fiber.StatusBadRequest)
	}

	if !cache.IsUnauthorizedRobotInList(params.RobotId) {
		logger.AddSystemLog("Unauthorized robot with id %v not found", params.RobotId)

		return c.SendStatus(fiber.StatusUnprocessableEntity)
	}

	unauthorizedRobot := cache.GetUnauthorizedRobotById(params.RobotId)

	if unauthorizedRobot.Id == "" {
		return c.SendStatus(fiber.StatusUnprocessableEntity)
	}

	newRobot := structs.Robot{
		Id:              params.RobotId,
		Type:            unauthorizedRobot.Type,
		Name:            uuid.New().String(),
		Address:         unauthorizedRobot.Address,
		Status:          utils.RobotStatusIdle,
		FirmwareVersion: unauthorizedRobot.FirmwareVersion,
		ConnectedAt:     unauthorizedRobot.ConnectedAt,
		CreatedAt:       unauthorizedRobot.CreatedAt,
	}

	cache.AddRobot(&newRobot)
	database.DB.Create(&newRobot)

	cache.RemoveUnauthorizedRobotById(params.RobotId)

	addRobotSSEMessage(&newRobot)

	logger.AddSystemLog("Robot authorized with id %v and type %v", params.RobotId, utils.GetRobotTypeString(unauthorizedRobot.Type))

	return c.SendStatus(fiber.StatusOK)
}

func DeleteRobot(c *fiber.Ctx) error {
	// swagger:operation DELETE /robot/{robotId} robot robotDelete
	// ---
	// summary: Delete robot.
	// consumes:
	// - application/json
	// produces:
	// - application/json
	// parameters:
	// - in: path
	//   name: robotId
	//   description: Robot id.
	//   required: true
	//   schema:
	//     type: string
	// responses:
	//   "200":
	//    description: Robot deleted
	//   "400":
	//    description: Invalid robot id

	var params structs.RobotIdParams

	if err := rsutils.ParamsParserHelper(c, &params); err != nil {
		return c.SendStatus(fiber.StatusBadRequest)
	}

	database.DB.Delete(&structs.Robot{}, "id = ?", params.RobotId)
	cache.RemoveRobotById(params.RobotId)

	robot.BroadcastSSEMessage(structs.SSEMessage{
		Cmd: utils.SSESentCmdRemoveRobot,
		Body: struct {
			RobotId    string
			TotalPages int
		}{
			RobotId:    params.RobotId,
			TotalPages: cache.GetAllRobots(rspagination.PageQuery{Page: 1}).TotalPages,
		},
	})

	logger.AddSystemLog("Robot deleted with id %v", params.RobotId)

	return c.SendStatus(fiber.StatusOK)
}

func DenyUnauthorizedRobot(c *fiber.Ctx) error {
	// swagger:operation DELETE /robot/deny/{robotId} robot robotDenyUnauthorizedRobot
	// ---
	// summary: Deny unauthorized robot.
	// consumes:
	// - application/json
	// produces:
	// - application/json
	// parameters:
	// - in: path
	//   name: robotId
	//   description: Robot id.
	//   required: true
	//   schema:
	//     type: string
	// responses:
	//   "200":
	//    description: Robot denied
	//   "400":
	//    description: Invalid robot id

	var params structs.RobotIdParams

	if err := rsutils.ParamsParserHelper(c, &params); err != nil {
		return c.SendStatus(fiber.StatusBadRequest)
	}

	cache.RemoveUnauthorizedRobotById(params.RobotId)

	robot.BroadcastSSEMessage(structs.SSEMessage{
		Cmd: utils.SSESentCmdRemoveUnauthorizedRobot,
		Body: struct {
			UnauthorizedRobotId string
			TotalPages          int
		}{
			UnauthorizedRobotId: params.RobotId,
			TotalPages:          cache.GetAllUnauthorizedRobots(rspagination.PageQuery{Page: 1}).TotalPages,
		},
	})

	logger.AddSystemLog("Unauthorized robot denied with id %v", params.RobotId)

	return c.SendStatus(fiber.StatusOK)
}

func UpdateRobot(c *fiber.Ctx) error {
	// swagger:operation PATCH /robot robot robotUpdate
	// ---
	// summary: Update robot.
	// consumes:
	// - application/json
	// produces:
	// - application/json
	// parameters:
	// - in: body
	//   name: body
	//   description: Update robot body.
	//   required: true
	//   schema:
	//     "$ref": "#/definitions/Robot"
	// responses:
	//   "200":
	//    description: Robot updated
	//   "400":
	//    description: Invalid request body
	//   "422":
	//    description: Robot not found

	var body structs.UpdateRobotBody

	if err := rsutils.BodyParserHelper(c, &body); err != nil {
		return c.SendStatus(fiber.StatusBadRequest)
	}

	if !cache.IsRobotInList(body.RobotId) {
		logger.AddSystemLog("Robot with id %v not found", body.RobotId)

		return c.SendStatus(fiber.StatusUnprocessableEntity)
	}

	foundRobot := cache.GetRobotById(body.RobotId)

	if foundRobot == nil || cache.IsRobotNameInList(body.RobotName) {
		return c.SendStatus(fiber.StatusUnprocessableEntity)
	}

	foundRobot.Name = body.RobotName

	database.DB.Model(&structs.Robot{}).
		Where("id = ?", foundRobot.Id).
		Update("name", foundRobot.Name)

	robot.BroadcastSSEMessage(structs.SSEMessage{
		Cmd:  utils.SSESentCmdRobotUpdated,
		Body: body,
	})

	logger.AddSystemLog("Robot with id %v name changed to %v", body.RobotId, body.RobotName)

	return c.SendStatus(fiber.StatusOK)
}
