package controller

import (
	"math"
	"strconv"
	"strings"

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

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

type ClosingController struct {
	ClosingService         service.ClosingService
	SapronakService        service.SapronakService
	ClosingKeuanganService service.ClosingKeuanganService
}

func NewClosingController(closingService service.ClosingService, sapronakService service.SapronakService, closingKeuanganService service.ClosingKeuanganService) *ClosingController {
	return &ClosingController{
		ClosingService:         closingService,
		SapronakService:        sapronakService,
		ClosingKeuanganService: closingKeuanganService,
	}
}

func (u *ClosingController) GetAll(c *fiber.Ctx) error {
	var projectStatus *int
	if raw := c.Query("project_status"); raw != "" {
		statusValue, err := strconv.Atoi(raw)
		if err != nil {
			return fiber.NewError(fiber.StatusBadRequest, "Invalid project_status")
		}
		projectStatus = &statusValue
	}

	var locationID *uint
	if raw := c.Query("location_id"); raw != "" {
		locationValue, err := strconv.Atoi(raw)
		if err != nil || locationValue <= 0 {
			return fiber.NewError(fiber.StatusBadRequest, "Invalid location_id")
		}
		locationUint := uint(locationValue)
		locationID = &locationUint
	}

	query := &validation.Query{
		Page:          c.QueryInt("page", 1),
		Limit:         c.QueryInt("limit", 10),
		Search:        c.Query("search", ""),
		ProjectStatus: projectStatus,
		LocationID:    locationID,
	}

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

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

	return c.Status(fiber.StatusOK).
		JSON(response.SuccessWithPaginate[dto.ClosingListItemDTO]{
			Code:    fiber.StatusOK,
			Status:  "success",
			Message: "Retrieved closing projects list successfully",
			Meta: response.Meta{
				Page:         query.Page,
				Limit:        query.Limit,
				TotalPages:   int64(math.Ceil(float64(totalResults) / float64(query.Limit))),
				TotalResults: totalResults,
			},
			Data: result,
		})
}

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

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

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

	return c.Status(fiber.StatusOK).
		JSON(response.Success{
			Code:    fiber.StatusOK,
			Status:  "success",
			Message: "Retrieved closing information successfully",
			Data:    result,
		})
}

func (u *ClosingController) GetOverheadByProjectFlockKandang(c *fiber.Ctx) error {
	projectParam := c.Params("project_flock_id")
	kandangParam := c.Params("project_flock_kandang_id")

	projectFlockID, err := strconv.Atoi(projectParam)
	if err != nil || projectFlockID <= 0 {
		return fiber.NewError(fiber.StatusBadRequest, "Invalid project_flock_id")
	}

	pfkID, err := strconv.Atoi(kandangParam)
	if err != nil || pfkID <= 0 {
		return fiber.NewError(fiber.StatusBadRequest, "Invalid project_flock_kandang_id")
	}

	kandangID := uint(pfkID)

	result, err := u.ClosingService.GetOverhead(c, uint(projectFlockID), &kandangID)
	if err != nil {
		return err
	}

	return c.Status(fiber.StatusOK).
		JSON(response.Success{
			Code:    fiber.StatusOK,
			Status:  "success",
			Message: "Get overhead by project flock kandang successfully",
			Data:    result,
		})
}

func (u *ClosingController) GetClosingSummary(c *fiber.Ctx) error {
	param := c.Params("projectFlockId")

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

	var kandangID *uint
	if raw := c.Query("kandang_id"); raw != "" {
		kandangInt, convErr := strconv.Atoi(raw)
		if convErr != nil || kandangInt <= 0 {
			return fiber.NewError(fiber.StatusBadRequest, "Invalid kandang_id")
		}
		kandangUint := uint(kandangInt)
		kandangID = &kandangUint
	}

	result, err := u.ClosingService.GetClosingSummary(c, uint(id), kandangID)
	if err != nil {
		return err
	}

	return c.Status(fiber.StatusOK).
		JSON(response.Success{
			Code:    fiber.StatusOK,
			Status:  "success",
			Message: "Retrieved project information successfully",
			Data:    result,
		})
}

func (u *ClosingController) GetPenjualan(c *fiber.Ctx) error {
	param := c.Params("project_flock_id")

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

	result, err := u.ClosingService.GetPenjualan(c, uint(projectFlockID), nil)
	if err != nil {
		return err
	}

	return c.Status(fiber.StatusOK).
		JSON(response.Success{
			Code:    fiber.StatusOK,
			Status:  "success",
			Message: "Get closing penjualan successfully",
			Data:    dto.ToPenjualanRealisasiResponseDTO(result),
		})
}

func (u *ClosingController) GetPenjualanByProjectFlockKandang(c *fiber.Ctx) error {
	projectParam := c.Params("project_flock_id")
	kandangParam := c.Params("project_flock_kandang_id")

	projectFlockID, err := strconv.Atoi(projectParam)
	if err != nil || projectFlockID <= 0 {
		return fiber.NewError(fiber.StatusBadRequest, "Invalid project_flock_id")
	}

	pfkID, err := strconv.Atoi(kandangParam)
	if err != nil || pfkID <= 0 {
		return fiber.NewError(fiber.StatusBadRequest, "Invalid project_flock_kandang_id")
	}

	kandangID := uint(pfkID)

	result, err := u.ClosingService.GetPenjualan(c, uint(projectFlockID), &kandangID)
	if err != nil {
		return err
	}

	return c.Status(fiber.StatusOK).
		JSON(response.Success{
			Code:    fiber.StatusOK,
			Status:  "success",
			Message: "Get closing penjualan by project flock kandang successfully",
			Data:    dto.ToPenjualanRealisasiResponseDTO(result),
		})
}

func (u *ClosingController) GetOverhead(c *fiber.Ctx) error {
	projectParam := c.Params("project_flock_id")
	kandangParam := c.Params("project_flock_kandang_id")

	projectFlockID, err := strconv.Atoi(projectParam)
	if err != nil || projectFlockID <= 0 {
		return fiber.NewError(fiber.StatusBadRequest, "Invalid project_flock_id")
	}

	var projectFlockKandangID *uint
	if kandangParam != "" {
		pfkID, err := strconv.Atoi(kandangParam)
		if err != nil || pfkID <= 0 {
			return fiber.NewError(fiber.StatusBadRequest, "Invalid project_flock_kandang_id")
		}
		kandangID := uint(pfkID)
		projectFlockKandangID = &kandangID
	}

	result, err := u.ClosingService.GetOverhead(c, uint(projectFlockID), projectFlockKandangID)
	if err != nil {
		return err
	}

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

func (u *ClosingController) GetClosingSapronak(c *fiber.Ctx) error {
	param := c.Params("projectFlockId")

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

	query := &validation.ClosingSapronakQuery{
		Type:   strings.ToLower(c.Query("type")),
		Page:   c.QueryInt("page", 1),
		Limit:  c.QueryInt("limit", 10),
		Search: c.Query("search"),
	}
	if raw := c.Query("kandang_id"); raw != "" {
		kandangInt, convErr := strconv.Atoi(raw)
		if convErr != nil || kandangInt <= 0 {
			return fiber.NewError(fiber.StatusBadRequest, "Invalid kandang_id")
		}
		kandangUint := uint(kandangInt)
		query.KandangID = &kandangUint
	}

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

	if query.Type != validation.SapronakTypeIncoming && query.Type != validation.SapronakTypeOutgoing {
		return fiber.NewError(fiber.StatusBadRequest, "type must be either incoming or outgoing")
	}

	result, totalResults, err := u.ClosingService.GetClosingSapronak(c, uint(id), query)
	if err != nil {
		return err
	}

	return c.Status(fiber.StatusOK).
		JSON(response.SuccessWithPaginate[dto.ClosingSapronakItemDTO]{
			Code:    fiber.StatusOK,
			Status:  "success",
			Message: "Retrieved closing report (sapronak) successfully",
			Meta: response.Meta{
				Page:         query.Page,
				Limit:        query.Limit,
				TotalPages:   int64(math.Ceil(float64(totalResults) / float64(query.Limit))),
				TotalResults: totalResults,
			},
			Data: result,
		})
}

func (u *ClosingController) GetClosingSapronakSummary(c *fiber.Ctx) error {
	param := c.Params("projectFlockId")

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

	query := &validation.ClosingSapronakQuery{
		Type:   strings.ToLower(c.Query("type")),
		Search: c.Query("search"),
	}
	if raw := c.Query("kandang_id"); raw != "" {
		kandangInt, convErr := strconv.Atoi(raw)
		if convErr != nil || kandangInt <= 0 {
			return fiber.NewError(fiber.StatusBadRequest, "Invalid kandang_id")
		}
		kandangUint := uint(kandangInt)
		query.KandangID = &kandangUint
	}

	if query.Type != validation.SapronakTypeIncoming && query.Type != validation.SapronakTypeOutgoing {
		return fiber.NewError(fiber.StatusBadRequest, "type must be either incoming or outgoing")
	}

	result, err := u.ClosingService.GetClosingSapronakSummary(c, uint(id), query)
	if err != nil {
		return err
	}

	return c.Status(fiber.StatusOK).
		JSON(response.Success{
			Code:    fiber.StatusOK,
			Status:  "success",
			Message: "Retrieved closing report (sapronak summary) successfully",
			Data:    result,
		})
}

func (u *ClosingController) GetSapronakByProject(c *fiber.Ctx) error {
	param := c.Params("project_flock_id")
	flag := c.Query("flag", "")

	projectID, err := strconv.Atoi(param)
	if err != nil || projectID <= 0 {
		return fiber.NewError(fiber.StatusBadRequest, "Invalid project_flock_id")
	}

	result, productFlags, err := u.SapronakService.GetSapronakByProject(c, uint(projectID), flag)
	if err != nil {
		return err
	}

	payload := dto.ToSapronakProjectAggregatedFromReports(result, flag, productFlags)

	return c.Status(fiber.StatusOK).
		JSON(response.Success{
			Code:    fiber.StatusOK,
			Status:  "success",
			Message: "Get perhitungan sapronak per project successfully",
			Data:    payload,
		})
}

func (u *ClosingController) GetSapronakByKandang(c *fiber.Ctx) error {
	projectParam := c.Params("project_flock_id")
	kandangParam := c.Params("project_flock_kandang_id")
	flag := c.Query("flag", "")

	projectID, err := strconv.Atoi(projectParam)
	if err != nil || projectID <= 0 {
		return fiber.NewError(fiber.StatusBadRequest, "Invalid project_flock_id")
	}
	pfkID, err := strconv.Atoi(kandangParam)
	if err != nil || pfkID <= 0 {
		return fiber.NewError(fiber.StatusBadRequest, "Invalid project_flock_kandang_id")
	}

	result, productFlags, err := u.SapronakService.GetSapronakByKandang(c, uint(projectID), uint(pfkID), flag)
	if err != nil {
		return err
	}

	payload := dto.ToSapronakProjectAggregatedFromReport(result, flag, productFlags)

	return c.Status(fiber.StatusOK).
		JSON(response.Success{
			Code:    fiber.StatusOK,
			Status:  "success",
			Message: "Get perhitungan sapronak per kandang successfully",
			Data:    payload,
		})
}

func (u *ClosingController) GetClosingKeuangan(c *fiber.Ctx) error {
	param := c.Params("projectFlockId")

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

	result, err := u.ClosingKeuanganService.GetClosingKeuangan(c, uint(projectFlockID))
	if err != nil {
		return err
	}

	return c.Status(fiber.StatusOK).
		JSON(response.Success{
			Code:    fiber.StatusOK,
			Status:  "success",
			Message: "Get closing keuangan successfully",
			Data:    result,
		})
}

func (u *ClosingController) GetClosingKeuanganByKandang(c *fiber.Ctx) error {
	projectParam := c.Params("project_flock_id")
	kandangParam := c.Params("project_flock_kandang_id")

	projectFlockID, err := strconv.Atoi(projectParam)
	if err != nil || projectFlockID <= 0 {
		return fiber.NewError(fiber.StatusBadRequest, "Invalid project_flock_id")
	}

	pfkID, err := strconv.Atoi(kandangParam)
	if err != nil || pfkID <= 0 {
		return fiber.NewError(fiber.StatusBadRequest, "Invalid project_flock_kandang_id")
	}

	result, err := u.ClosingKeuanganService.GetClosingKeuanganByKandang(c, uint(projectFlockID), uint(pfkID))
	if err != nil {
		return err
	}

	return c.Status(fiber.StatusOK).
		JSON(response.Success{
			Code:    fiber.StatusOK,
			Status:  "success",
			Message: "Get closing keuangan by kandang successfully",
			Data:    result,
		})
}

func (u *ClosingController) GetExpeditionHPP(c *fiber.Ctx) error {
	param := c.Params("project_flock_id")

	projectFlockID, err := strconv.Atoi(param)
	if err != nil || projectFlockID <= 0 {
		return fiber.NewError(fiber.StatusBadRequest, "Invalid project_flock_id")
	}

	var projectFlockKandangID *uint
	if raw := c.Query("project_flock_kandang_id"); raw != "" {
		idInt, convErr := strconv.Atoi(raw)
		if convErr != nil || idInt <= 0 {
			return fiber.NewError(fiber.StatusBadRequest, "Invalid project_flock_kandang_id")
		}
		idUint := uint(idInt)
		projectFlockKandangID = &idUint
	}

	result, err := u.ClosingService.GetExpeditionHPP(c, uint(projectFlockID), projectFlockKandangID)
	if err != nil {
		return err
	}

	return c.Status(fiber.StatusOK).
		JSON(response.Success{
			Code:    fiber.StatusOK,
			Status:  "success",
			Message: "Get expedition HPP successfully",
			Data:    result,
		})
}

func (u *ClosingController) GetExpeditionHPPByKandang(c *fiber.Ctx) error {
	projectParam := c.Params("project_flock_id")
	kandangParam := c.Params("project_flock_kandang_id")

	projectFlockID, err := strconv.Atoi(projectParam)
	if err != nil || projectFlockID <= 0 {
		return fiber.NewError(fiber.StatusBadRequest, "Invalid project_flock_id")
	}

	pfkID, err := strconv.Atoi(kandangParam)
	if err != nil || pfkID <= 0 {
		return fiber.NewError(fiber.StatusBadRequest, "Invalid project_flock_kandang_id")
	}

	kandangID := uint(pfkID)

	result, err := u.ClosingService.GetExpeditionHPP(c, uint(projectFlockID), &kandangID)
	if err != nil {
		return err
	}

	return c.Status(fiber.StatusOK).
		JSON(response.Success{
			Code:    fiber.StatusOK,
			Status:  "success",
			Message: "Get expedition HPP successfully",
			Data:    result,
		})
}

func (u *ClosingController) GetClosingDataProduksi(c *fiber.Ctx) error {
	param := c.Params("projectFlockId")

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

	var kandangID *uint
	if raw := c.Query("kandang_id"); raw != "" {
		kandangInt, convErr := strconv.Atoi(raw)
		if convErr != nil || kandangInt <= 0 {
			return fiber.NewError(fiber.StatusBadRequest, "Invalid kandang_id")
		}
		kandangUint := uint(kandangInt)
		kandangID = &kandangUint

	}

	result, err := u.ClosingService.GetClosingDataProduksi(c, uint(id), kandangID)
	if err != nil {
		return err
	}

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