package controller

import (
	"fmt"
	"math"
	"strconv"

	"gitlab.com/mbugroup/lti-api.git/internal/modules/production/transfer_layings/dto"
	service "gitlab.com/mbugroup/lti-api.git/internal/modules/production/transfer_layings/services"
	validation "gitlab.com/mbugroup/lti-api.git/internal/modules/production/transfer_layings/validations"
	"gitlab.com/mbugroup/lti-api.git/internal/response"
	"gitlab.com/mbugroup/lti-api.git/internal/utils"

	"github.com/gofiber/fiber/v2"
)

type TransferLayingController struct {
	TransferLayingService service.TransferLayingService
}

func NewTransferLayingController(transferLayingService service.TransferLayingService) *TransferLayingController {
	return &TransferLayingController{
		TransferLayingService: transferLayingService,
	}
}

func (u *TransferLayingController) GetAll(c *fiber.Ctx) error {
	query := &validation.Query{
		Page:             c.QueryInt("page", 1),
		Limit:            c.QueryInt("limit", 10),
		Search:           c.Query("search", ""),
		StartDate:        c.Query("start_date", ""),
		EndDate:          c.Query("end_date", ""),
		FlockSource:      utils.ParseQueryUintArray(c.Query("flock_source", "")),
		FlockDestination: utils.ParseQueryUintArray(c.Query("flock_destination", "")),
		Status:           utils.ParseQueryArray(c.Query("status", "")),
	}

	if query.Page < 1 || query.Limit < 1 {
		return fiber.NewError(fiber.StatusBadRequest, "page and limit must be greater than 0")
	}

	result, totalResults, err := u.TransferLayingService.GetAll(c, query)
	if err != nil {
		return err
	}

	data := make([]dto.TransferLayingDetailDTO, len(result))
	for i, transfer := range result {
		data[i] = dto.ToTransferLayingDetailDTOWithSingleApproval(transfer, transfer.LatestApproval)
	}

	return c.Status(fiber.StatusOK).
		JSON(response.SuccessWithPaginate[dto.TransferLayingDetailDTO]{
			Code:    fiber.StatusOK,
			Status:  "success",
			Message: "Get all transferLayings successfully",
			Meta: response.Meta{
				Page:         query.Page,
				Limit:        query.Limit,
				TotalPages:   int64(math.Ceil(float64(totalResults) / float64(query.Limit))),
				TotalResults: totalResults,
			},
			Data: data,
		})
}

func (u *TransferLayingController) GetOne(c *fiber.Ctx) error {
	param := c.Params("id")

	id, err := strconv.Atoi(param)
	if err != nil {
		return fiber.NewError(fiber.StatusBadRequest, "Invalid Id")
	}

	result, approval, err := u.TransferLayingService.GetOne(c, uint(id))
	if err != nil {
		return err
	}

	return c.Status(fiber.StatusOK).
		JSON(response.Success{
			Code:    fiber.StatusOK,
			Status:  "success",
			Message: "Get transferLaying successfully",
			Data:    dto.ToTransferLayingDetailDTOWithSingleApproval(*result, approval),
		})
}

func (u *TransferLayingController) CreateOne(c *fiber.Ctx) error {
	req := new(validation.Create)

	if err := c.BodyParser(req); err != nil {
		return fiber.NewError(fiber.StatusBadRequest, "Format permintaan tidak valid")
	}

	result, err := u.TransferLayingService.CreateOne(c, req)
	if err != nil {
		return err
	}

	return c.Status(fiber.StatusCreated).
		JSON(response.Success{
			Code:    fiber.StatusCreated,
			Status:  "success",
			Message: "Berhasil membuat transfer laying",
			Data:    dto.ToTransferLayingListDTO(*result),
		})
}

func (u *TransferLayingController) UpdateOne(c *fiber.Ctx) error {
	req := new(validation.Update)
	param := c.Params("id")

	id, err := strconv.Atoi(param)
	if err != nil {
		return fiber.NewError(fiber.StatusBadRequest, "Invalid Id")
	}

	if err := c.BodyParser(req); err != nil {
		return fiber.NewError(fiber.StatusBadRequest, fmt.Sprintf("Invalid request body: %s", err.Error()))
	}

	result, err := u.TransferLayingService.UpdateOne(c, req, uint(id))
	if err != nil {
		return err
	}

	return c.Status(fiber.StatusOK).
		JSON(response.Success{
			Code:    fiber.StatusOK,
			Status:  "success",
			Message: "Update transferLaying successfully",
			Data:    dto.ToTransferLayingDetailDTOWithSingleApproval(*result, result.LatestApproval),
		})
}

func (u *TransferLayingController) DeleteOne(c *fiber.Ctx) error {
	param := c.Params("id")

	id, err := strconv.Atoi(param)
	if err != nil {
		return fiber.NewError(fiber.StatusBadRequest, "Invalid Id")
	}

	if err := u.TransferLayingService.DeleteOne(c, uint(id)); err != nil {
		return err
	}

	return c.Status(fiber.StatusOK).
		JSON(response.Common{
			Code:    fiber.StatusOK,
			Status:  "success",
			Message: "Delete transferLaying successfully",
		})
}

func (u *TransferLayingController) Approval(c *fiber.Ctx) error {
	req := new(validation.Approve)
	if err := c.BodyParser(req); err != nil {
		return fiber.NewError(fiber.StatusBadRequest, "Invalid request body")
	}

	results, err := u.TransferLayingService.Approval(c, req)
	if err != nil {
		return err
	}

	var (
		data    interface{}
		message = "Submit transfer laying approval successfully"
	)
	if len(results) == 1 {
		data = dto.ToTransferLayingListDTO(results[0])
	} else {
		message = "Submit transfer laying approvals successfully"
		data = dto.ToTransferLayingListDTOs(results)
	}

	return c.Status(fiber.StatusOK).
		JSON(response.Success{
			Code:    fiber.StatusOK,
			Status:  "success",
			Message: message,
			Data:    data,
		})
}

func (u *TransferLayingController) Execute(c *fiber.Ctx) error {
	param := c.Params("id")

	id, err := strconv.Atoi(param)
	if err != nil {
		return fiber.NewError(fiber.StatusBadRequest, "Invalid Id")
	}

	result, err := u.TransferLayingService.Execute(c, uint(id))
	if err != nil {
		return err
	}

	return c.Status(fiber.StatusOK).
		JSON(response.Success{
			Code:    fiber.StatusOK,
			Status:  "success",
			Message: "Execute transfer laying successfully",
			Data:    dto.ToTransferLayingDetailDTOWithSingleApproval(*result, result.LatestApproval),
		})
}

func (u *TransferLayingController) Unexecute(c *fiber.Ctx) error {
	param := c.Params("id")

	id, err := strconv.Atoi(param)
	if err != nil {
		return fiber.NewError(fiber.StatusBadRequest, "Invalid Id")
	}

	result, err := u.TransferLayingService.Unexecute(c, uint(id))
	if err != nil {
		return err
	}

	return c.Status(fiber.StatusOK).
		JSON(response.Success{
			Code:    fiber.StatusOK,
			Status:  "success",
			Message: "Unexecute transfer laying successfully",
			Data:    dto.ToTransferLayingDetailDTOWithSingleApproval(*result, result.LatestApproval),
		})
}

func (u *TransferLayingController) GetAvailableQtyPerKandang(c *fiber.Ctx) error {
	projectFlockID, err := strconv.ParseUint(c.Params("project_flock_id"), 10, 32)
	if err != nil {
		return fiber.NewError(fiber.StatusBadRequest, "Invalid project_flock_id")
	}

	pf, kandangQtyMap, err := u.TransferLayingService.GetAvailableQtyPerKandang(c, uint(projectFlockID))
	if err != nil {
		return err
	}

	// Build kandang list response
	kandangs := make([]dto.KandangAvailableQtyDTO, 0, len(kandangQtyMap))
	for kandangPFID, qty := range kandangQtyMap {
		kandangs = append(kandangs, dto.KandangAvailableQtyDTO{
			ProjectFlockKandangId: kandangPFID,
			AvailableQty:          qty,
		})
	}

	resp := dto.AvailableQtyForTransferDTO{
		ProjectFlockId:   pf.Id,
		ProjectFlockCode: pf.FlockName,
		Category:         pf.Category,
		Kandangs:         kandangs,
	}

	return c.Status(fiber.StatusOK).
		JSON(response.Success{
			Code:    fiber.StatusOK,
			Status:  "success",
			Message: "Get available quantity successfully",
			Data:    resp,
		})
}

func (u *TransferLayingController) GetMaxTargetQtyPerKandang(c *fiber.Ctx) error {
	projectFlockID, err := strconv.ParseUint(c.Params("project_flock_id"), 10, 32)
	if err != nil {
		return fiber.NewError(fiber.StatusBadRequest, "Invalid project_flock_id")
	}
	kandangMaxTargetQty, err := u.TransferLayingService.GetMaxTargetQtyPerKandang(c, uint(projectFlockID))
	if err != nil {
		return err
	}

	kandangs := make([]dto.KandangMaxTargetQtyDTO, 0, len(kandangMaxTargetQty))
	for pfkId, maxTargetQty := range kandangMaxTargetQty {
		kandangs = append(kandangs, dto.ToKandangMaxTargetQtyDTO(pfkId, maxTargetQty))
	}

	resp := dto.ToMaxTargetQtyForTransferDTO(uint(projectFlockID), kandangs)

	return c.Status(fiber.StatusOK).
		JSON(response.Success{
			Code:    fiber.StatusOK,
			Status:  "success",
			Message: "Get max target quantity successfully",
			Data:    resp,
		})
}
