package controller

import (
	"math"

	"gitlab.com/mbugroup/lti-api.git/internal/modules/production/uniformities/dto"
	service "gitlab.com/mbugroup/lti-api.git/internal/modules/production/uniformities/services"
	utypes "gitlab.com/mbugroup/lti-api.git/internal/modules/production/uniformities/types"
	validation "gitlab.com/mbugroup/lti-api.git/internal/modules/production/uniformities/validations"
	"gitlab.com/mbugroup/lti-api.git/internal/response"

	"github.com/gofiber/fiber/v2"
	entity "gitlab.com/mbugroup/lti-api.git/internal/entities"
)

type UniformityController struct {
	UniformityService service.UniformityService
}

func NewUniformityController(uniformityService service.UniformityService) *UniformityController {
	return &UniformityController{
		UniformityService: uniformityService,
	}
}

func (u *UniformityController) GetAll(c *fiber.Ctx) error {
	query, err := validation.ParseQuery(c)
	if err != nil {
		return err
	}

	result, totalResults, err := u.UniformityService.GetAll(c, query)
	if err != nil {
		return err
	}
	standards, err := u.UniformityService.MapStandards(c, result)
	if err != nil {
		return err
	}
	documents, err := u.UniformityService.MapDocuments(c, result)
	if err != nil {
		return err
	}
	var charts map[uint]utypes.UniformityChartData
	if query.WithChart {
		charts, err = u.UniformityService.MapCharts(c, result)
		if err != nil {
			return err
		}
	}

	return c.Status(fiber.StatusOK).
		JSON(response.SuccessWithPaginate[dto.UniformityListDTO]{
			Code:    fiber.StatusOK,
			Status:  "success",
			Message: "Get all production uniformities successfully",
			Meta: response.Meta{
				Page:         query.Page,
				Limit:        query.Limit,
				TotalPages:   int64(math.Ceil(float64(totalResults) / float64(query.Limit))),
				TotalResults: totalResults,
				Filters:      dto.BuildUniformityFilters(query),
			},
			Data: dto.ToUniformityListDTOsWithStandard(result, standards, documents, charts),
		})
}

func (u *UniformityController) GetOne(c *fiber.Ctx) error {
	id, err := validation.ParseIDParam(c, "id")
	if err != nil {
		return err
	}

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

	withDetails := c.QueryBool("with_details", false)
	calculation := utypes.UniformityCalculation{}
	var document *entity.Document
	var documentURL string
	var meanWeight float64
	if result.MeanUp > 0 {
		meanWeight = math.Round(result.MeanUp / 1.10)
	}
	if withDetails {
		var err error
		calculation, document, documentURL, err = u.UniformityService.CalculateUniformityFromDocument(c, id)
		if err != nil {
			return err
		}
	} else {
		calculation = utypes.UniformityCalculation{
			ChickQtyOfWeight: result.ChickQtyOfWeight,
			MeanWeight:       meanWeight,
			MeanDown:         result.MeanDown,
			MeanUp:           result.MeanUp,
			UniformQty:       result.UniformQty,
			OutsideQty:       result.NotUniformQty,
			Uniformity:       result.Uniformity,
			Cv:               result.Cv,
		}
		document, documentURL, err = u.UniformityService.GetDocumentInfo(c, id)
		if err != nil {
			return err
		}
	}

	standard, err := u.UniformityService.GetStandard(c, result)
	if err != nil {
		return err
	}
	var standardDTO *dto.UniformityStandardDTO
	if standard != nil {
		standardDTO = &dto.UniformityStandardDTO{
			MeanWeight: standard.MeanWeight,
			Uniformity: standard.Uniformity,
		}
	}

	return c.Status(fiber.StatusOK).
		JSON(response.Success{
			Code:    fiber.StatusOK,
			Status:  "success",
			Message: "Get production uniformity successfully",
			Data:    dto.ToUniformityDetailDTO(*result, calculation, document, documentURL, standardDTO),
		})
}

func (u *UniformityController) CreateOne(c *fiber.Ctx) error {
	req, file, err := validation.ParseCreate(c)
	if err != nil {
		return err
	}

	rows, err := u.UniformityService.ParseBodyWeightExcel(c, file)
	if err != nil {
		return err
	}

	calculation, err := u.UniformityService.ComputeUniformity(rows)
	if err != nil {
		return err
	}

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

	document := dto.NewDocumentForResponse(file.Filename)
	standard, err := u.UniformityService.GetStandard(c, result)
	if err != nil {
		return err
	}
	var standardDTO *dto.UniformityStandardDTO
	if standard != nil {
		standardDTO = &dto.UniformityStandardDTO{
			MeanWeight: standard.MeanWeight,
			Uniformity: standard.Uniformity,
		}
	}

	return c.Status(fiber.StatusCreated).
		JSON(response.Success{
			Code:    fiber.StatusCreated,
			Status:  "success",
			Message: "Create uniformity successfully",
			Data:    dto.ToUniformityDetailDTO(*result, calculation, document, "", standardDTO),
		})
}

func (u *UniformityController) UploadBodyWeightExcel(c *fiber.Ctx) error {
	files, err := validation.ParseUploadFiles(c)
	if err != nil {
		return err
	}

	rows, err := u.UniformityService.ParseBodyWeightExcel(c, files[0])
	if err != nil {
		return err
	}

	calculation, err := u.UniformityService.ComputeUniformity(rows)
	if err != nil {
		return err
	}

	return c.Status(fiber.StatusOK).
		JSON(response.Success{
			Code:    fiber.StatusOK,
			Status:  "success",
			Message: "Uniformity verified successfully",
			Data:    dto.ToUniformityVerificationDTO(calculation),
		})
}

func (u *UniformityController) UpdateOne(c *fiber.Ctx) error {
	id, err := validation.ParseIDParam(c, "id")
	if err != nil {
		return err
	}

	req, file, err := validation.ParseUpdate(c)
	if err != nil {
		return err
	}

	var rows []service.BodyWeightExcelRow
	if file != nil {
		parsed, err := u.UniformityService.ParseBodyWeightExcel(c, file)
		if err != nil {
			return err
		}
		rows = parsed
	}

	result, err := u.UniformityService.UpdateOne(c, req, id, file, rows)
	if err != nil {
		return err
	}

	standard, err := u.UniformityService.GetStandard(c, result)
	if err != nil {
		return err
	}
	var standardDTO *dto.UniformityStandardDTO
	if standard != nil {
		standardDTO = &dto.UniformityStandardDTO{
			MeanWeight: standard.MeanWeight,
			Uniformity: standard.Uniformity,
		}
	}

	calculation := utypes.UniformityCalculation{
		ChickQtyOfWeight: result.ChickQtyOfWeight,
		MeanWeight:       math.Round(result.MeanUp / 1.10),
		MeanDown:         result.MeanDown,
		MeanUp:           result.MeanUp,
		UniformQty:       result.UniformQty,
		OutsideQty:       result.NotUniformQty,
		Uniformity:       result.Uniformity,
		Cv:               result.Cv,
	}
	var document *entity.Document

	return c.Status(fiber.StatusOK).
		JSON(response.Success{
			Code:    fiber.StatusOK,
			Status:  "success",
			Message: "Update uniformity successfully",
			Data:    dto.ToUniformityDetailDTO(*result, calculation, document, "", standardDTO),
		})
}

func (u *UniformityController) DeleteOne(c *fiber.Ctx) error {
	id, err := validation.ParseIDParam(c, "id")
	if err != nil {
		return err
	}

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

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

func (u *UniformityController) Approve(c *fiber.Ctx) error {
	req, err := validation.ParseApprove(c)
	if err != nil {
		return err
	}

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

	var (
		data    interface{}
		message = "Submit uniformity approvals successfully"
	)

	if len(results) == 1 {
		message = "Submit uniformity approval successfully"
		data = dto.ToUniformityListDTOs(results)[0]
	} else {
		data = dto.ToUniformityListDTOs(results)
	}

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