package controller

import (
	"fmt"
	"math"
	"strconv"
	"strings"
	"time"

	"gitlab.com/mbugroup/lti-api.git/internal/common/exportprogress"
	"gitlab.com/mbugroup/lti-api.git/internal/modules/production/recordings/dto"
	service "gitlab.com/mbugroup/lti-api.git/internal/modules/production/recordings/services"
	validation "gitlab.com/mbugroup/lti-api.git/internal/modules/production/recordings/validations"
	"gitlab.com/mbugroup/lti-api.git/internal/response"

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

type RecordingController struct {
	RecordingService service.RecordingService
}

func NewRecordingController(recordingService service.RecordingService) *RecordingController {
	return &RecordingController{
		RecordingService: recordingService,
	}
}

func (u *RecordingController) GetAll(c *fiber.Ctx) error {
	projectFlockKandangID := c.QueryInt("project_flock_kandang_id", 0)
	exportType := strings.TrimSpace(c.Query("export"))

	if exportprogress.IsProgressExportRequest(c) {
		query, err := exportprogress.ParseQuery(c)
		if err != nil {
			return err
		}
		rows, err := u.RecordingService.GetProgressRows(c, query)
		if err != nil {
			return err
		}
		content, err := exportprogress.BuildWorkbook("Recordings", query, rows)
		if err != nil {
			return fiber.NewError(fiber.StatusInternalServerError, "failed to generate progress excel file")
		}
		filename := fmt.Sprintf("recordings_progress_%s.xlsx", time.Now().Format("20060102_150405"))
		c.Set("Content-Type", "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet")
		c.Set("Content-Disposition", fmt.Sprintf(`attachment; filename="%s"`, filename))
		return c.Status(fiber.StatusOK).Send(content)
	}

	page := c.QueryInt("page", 1)
	limit := c.QueryInt("limit", 10)
	offset := (page - 1) * limit

	query := &validation.Query{
		Page:                 page,
		Limit:                limit,
		Offset:               offset,
		Search:               strings.TrimSpace(c.Query("search")),
		ProjectFlockId:       uint(c.QueryInt("project_flock_id", 0)),
		AreaId:               uint(c.QueryInt("area_id", 0)),
		LocationId:           uint(c.QueryInt("location_id", 0)),
		KandangId:            uint(c.QueryInt("kandang_id", 0)),
		ProjectFlockCategory: strings.TrimSpace(c.Query("project_flock_category")),
		ApprovalStatus:       strings.TrimSpace(c.Query("approval_status")),
	}
	if projectFlockKandangID > 0 {
		query.ProjectFlockKandangId = uint(projectFlockKandangID)
	}

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

	listDTO := dto.ToRecordingListDTOs(result)
	if strings.EqualFold(exportType, "excel") {
		return exportRecordingListExcel(c, listDTO)
	}

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

func (u *RecordingController) 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, err := u.RecordingService.GetOne(c, uint(id))
	if err != nil {
		return err
	}

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

func (u *RecordingController) GetNextDay(c *fiber.Ctx) error {
	req := new(validation.GetRecordingNextDay)

	if err := c.QueryParser(req); err != nil {
		return fiber.NewError(fiber.StatusBadRequest, "Invalid query params")
	}
	if req.ProjectFlockKandangId == 0 {
		return fiber.NewError(fiber.StatusBadRequest, "project_flock_kandang_id is required")
	}
	if req.RecordTime == nil || strings.TrimSpace(*req.RecordTime) == "" {
		return fiber.NewError(fiber.StatusBadRequest, "record_date is required")
	}
	recordTime, err := time.Parse("2006-01-02", strings.TrimSpace(*req.RecordTime))
	if err != nil {
		return fiber.NewError(fiber.StatusBadRequest, "record_time must be in YYYY-MM-DD format")
	}
	req.RecordTimeValue = &recordTime
	nextDay, err := u.RecordingService.GetNextDay(c, req)
	if err != nil {
		return err
	}
	projectFlockID := req.ProjectFlockKandangId
	return c.Status(fiber.StatusOK).
		JSON(response.Success{
			Code:    fiber.StatusOK,
			Status:  "success",
			Message: "Get next recording day successfully",
			Data: fiber.Map{
				"project_flock_kandang_id": projectFlockID,
				"next_day":                 nextDay,
			},
		})
}

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

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

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

	return c.Status(fiber.StatusCreated).
		JSON(response.Success{
			Code:    fiber.StatusCreated,
			Status:  "success",
			Message: "Create recording successfully",
			Data:    dto.ToRecordingDetailDTO(*result),
		})
}

func (u *RecordingController) 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, "Invalid request body")
	}

	result, err := u.RecordingService.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 recording successfully",
			Data:    dto.ToRecordingDetailDTO(*result),
		})
}

func (u *RecordingController) Approve(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.RecordingService.Approval(c, req)
	if err != nil {
		return err
	}

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

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

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

func (u *RecordingController) 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.RecordingService.DeleteOne(c, uint(id)); err != nil {
		return err
	}

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