package controller

import (
	"math"
	"mime/multipart"
	"strconv"

	"gitlab.com/mbugroup/lti-api.git/internal/modules/daily-checklists/dto"
	service "gitlab.com/mbugroup/lti-api.git/internal/modules/daily-checklists/services"
	validation "gitlab.com/mbugroup/lti-api.git/internal/modules/daily-checklists/validations"
	kandangDTO "gitlab.com/mbugroup/lti-api.git/internal/modules/master/kandangs/dto"
	"gitlab.com/mbugroup/lti-api.git/internal/response"

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

type DailyChecklistController struct {
	DailyChecklistService service.DailyChecklistService
}

func NewDailyChecklistController(dailyChecklistService service.DailyChecklistService) *DailyChecklistController {
	return &DailyChecklistController{
		DailyChecklistService: dailyChecklistService,
	}
}

func (u *DailyChecklistController) GetAll(c *fiber.Ctx) error {
	query := &validation.Query{
		Page:   c.QueryInt("page", 1),
		Limit:  c.QueryInt("limit", 10),
		Search: c.Query("search", ""),
	}
	query.DateFrom = c.Query("date_from", "")
	query.DateTo = c.Query("date_to", "")
	query.Status = c.Query("status", "")

	if kandangParam := c.Query("kandang_id", ""); kandangParam != "" {
		kandangID, err := strconv.ParseUint(kandangParam, 10, 64)
		if err != nil {
			return fiber.NewError(fiber.StatusBadRequest, "Invalid kandang_id")
		}
		value := uint(kandangID)
		query.KandangID = &value
	}

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

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

	responseData := make([]dto.DailyChecklistListDTO, len(result))
	for i, item := range result {
		var name string
		if item.Name != nil {
			name = *item.Name
		}

		var status string
		if item.Status != nil {
			status = *item.Status
		}

		// var kandang *kandangDTO.KandangRelationDTO
		var kandang *kandangDTO.KandangGroupRelationDTO
		if item.Kandang.Id != 0 {
			// mapped := kandangDTO.ToKandangRelationDTO(item.Kandang)x
			mapped := kandangDTO.ToKandangGroupRelationDTO(item.Kandang)
			kandang = &mapped
		}

		responseData[i] = dto.DailyChecklistListDTO{
			Id:            item.ID,
			Name:          name,
			Status:        status,
			Category:      item.Category,
			RejectReason:  item.RejectReason,
			Date:          item.Date,
			Kandang:       kandang,
			CreatedUser:   nil,
			CreatedAt:     item.CreatedAt,
			UpdatedAt:     item.UpdatedAt,
			TotalPhase:    item.TotalPhase,
			TotalActivity: item.TotalActivity,
			Progress:      item.Progress,
		}
	}

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

func (u *DailyChecklistController) GetSummary(c *fiber.Ctx) error {
	query := &validation.SummaryQuery{
		DateFrom: c.Query("date_from"),
		DateTo:   c.Query("date_to"),
		Category: c.Query("category"),
	}

	if query.DateFrom == "" || query.DateTo == "" {
		return fiber.NewError(fiber.StatusBadRequest, "date_from and date_to are required")
	}

	if kandangParam := c.Query("kandang_id"); kandangParam != "" {
		kandangID, err := strconv.ParseUint(kandangParam, 10, 64)
		if err != nil {
			return fiber.NewError(fiber.StatusBadRequest, "Invalid kandang_id")
		}
		value := uint(kandangID)
		query.KandangID = &value
	}

	result, err := u.DailyChecklistService.GetSummary(c, query)
	if err != nil {
		return err
	}

	type summaryResponse struct {
		PerformanceOverview []dto.DailyChecklistPerformanceOverviewDTO `json:"performance_overview"`
		TrackingABK         []dto.DailyChecklistSummaryDTO             `json:"tracking_abk"`
	}

	performanceMap := make(map[uint]*dto.DailyChecklistPerformanceOverviewDTO)
	tracking := make([]dto.DailyChecklistSummaryDTO, len(result))

	for i, summary := range result {
		tracking[i] = dto.DailyChecklistSummaryDTO{
			EmployeeID:     summary.EmployeeID,
			EmployeeName:   summary.EmployeeName,
			KandangID:      summary.KandangID,
			KandangName:    summary.KandangName,
			TotalActivity:  summary.TotalActivity,
			ActivityDone:   summary.ActivityDone,
			ActivityLeft:   summary.ActivityLeft,
			CompletionRate: summary.CompletionRate,
			LastActivity:   summary.LastActivity,
		}

		if _, ok := performanceMap[summary.EmployeeID]; !ok {
			performanceMap[summary.EmployeeID] = &dto.DailyChecklistPerformanceOverviewDTO{
				EmployeeID:   summary.EmployeeID,
				EmployeeName: summary.EmployeeName,
				Kandang: dto.DailyChecklistReportEntityDTO{
					Id:   summary.KandangID,
					Name: summary.KandangName,
				},
			}
		}

		performanceMap[summary.EmployeeID].TotalActivity += summary.TotalActivity
		performanceMap[summary.EmployeeID].ActivityDone += summary.ActivityDone
		performanceMap[summary.EmployeeID].ActivityLeft += summary.ActivityLeft
	}

	performance := make([]dto.DailyChecklistPerformanceOverviewDTO, 0, len(performanceMap))
	for _, v := range performanceMap {
		performance = append(performance, *v)
	}

	return c.Status(fiber.StatusOK).
		JSON(response.Success{
			Code:    fiber.StatusOK,
			Status:  "success",
			Message: "Get daily checklist summary successfully",
			Data: summaryResponse{
				PerformanceOverview: performance,
				TrackingABK:         tracking,
			},
		})
}

func (u *DailyChecklistController) GetReport(c *fiber.Ctx) error {
	query := &validation.ReportQuery{
		Page:  c.QueryInt("page", 1),
		Limit: c.QueryInt("limit", 10),
		Month: c.QueryInt("bulan", 0),
		Year:  c.QueryInt("tahun", 0),
	}

	parseUintParam := func(param string) (*uint, error) {
		if param == "" {
			return nil, nil
		}
		value, err := strconv.ParseUint(param, 10, 64)
		if err != nil {
			return nil, err
		}
		u := uint(value)
		return &u, nil
	}

	if val, err := parseUintParam(c.Query("area_id", "")); err != nil {
		return fiber.NewError(fiber.StatusBadRequest, "Invalid area_id")
	} else {
		query.AreaID = val
	}

	if val, err := parseUintParam(c.Query("location_id", "")); err != nil {
		return fiber.NewError(fiber.StatusBadRequest, "Invalid location_id")
	} else {
		query.LocationID = val
	}

	if val, err := parseUintParam(c.Query("kandang_id", "")); err != nil {
		return fiber.NewError(fiber.StatusBadRequest, "Invalid kandang_id")
	} else {
		query.KandangID = val
	}

	if val, err := parseUintParam(c.Query("employee_id", "")); err != nil {
		return fiber.NewError(fiber.StatusBadRequest, "Invalid employee_id")
	} else {
		query.EmployeeID = val
	}

	if val, err := parseUintParam(c.Query("phase_id", "")); err != nil {
		return fiber.NewError(fiber.StatusBadRequest, "Invalid phase_id")
	} else {
		query.PhaseID = val
	}

	if query.Month == 0 || query.Year == 0 {
		return fiber.NewError(fiber.StatusBadRequest, "bulan and tahun are required")
	}

	result, totalResults, err := u.DailyChecklistService.GetReport(c, query)
	withoutActivities := func(src map[string]int) map[string]int {
		if src == nil {
			return map[string]int{}
		}
		return src
	}

	if err != nil {
		return err
	}

	responseData := make([]dto.DailyChecklistReportDTO, len(result))
	for i, item := range result {
		responseData[i] = dto.DailyChecklistReportDTO{
			Area: dto.DailyChecklistReportEntityDTO{
				Id:   item.AreaID,
				Name: item.AreaName,
			},
			Farm: dto.DailyChecklistReportEntityDTO{
				Id:   item.LocationID,
				Name: item.LocationName,
			},
			Kandang: dto.DailyChecklistReportEntityDTO{
				Id:   item.KandangID,
				Name: item.KandangName,
			},
			ABK: dto.DailyChecklistReportEntityDTO{
				Id:   item.EmployeeID,
				Name: item.EmployeeName,
			},
			Phase:           item.PhaseName,
			DailyActivities: withoutActivities(item.DailyActivities),
			Summary: dto.DailyChecklistReportSummaryDTO{
				TotalChecklist:    item.Summary.TotalChecklist,
				JumlahHariEfektif: item.Summary.JumlahHariEfektif,
				AbkPercentage:     item.Summary.AbkPercentage,
				KandangPercentage: item.Summary.KandangPercentage,
				Kategori: dto.DailyChecklistReportCategoryDTO{
					Kurang: item.Summary.Category.Kurang,
					Cukup:  item.Summary.Category.Cukup,
					Baik:   item.Summary.Category.Baik,
				},
			},
		}
	}

	return c.Status(fiber.StatusOK).
		JSON(response.SuccessWithPaginate[dto.DailyChecklistReportDTO]{
			Code:    fiber.StatusOK,
			Status:  "success",
			Message: "Get daily checklist report successfully",
			Meta: response.Meta{
				Page:         query.Page,
				Limit:        query.Limit,
				TotalPages:   int64(math.Ceil(float64(totalResults) / float64(query.Limit))),
				TotalResults: totalResults,
			},
			Data: responseData,
		})
}
func (u *DailyChecklistController) GetOne(c *fiber.Ctx) error {
	param := c.Params("idDailyChecklist")

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

	detail, err := u.DailyChecklistService.GetDetail(c, uint(id))
	if err != nil {
		return err
	}

	documentDTOs := make([]dto.DailyChecklistDocumentDTO, len(detail.DocumentURLs))
	for i, doc := range detail.DocumentURLs {
		documentDTOs[i] = dto.DailyChecklistDocumentDTO{
			Id:   doc.ID,
			Name: doc.Name,
			Size: doc.Size,
			URL:  doc.URL,
		}
	}

	return c.Status(fiber.StatusOK).
		JSON(response.Success{
			Code:    fiber.StatusOK,
			Status:  "success",
			Message: "Get dailyChecklist successfully",
			Data:    dto.ToDailyChecklistDetailDTO(detail.Checklist, detail.Phases, detail.Tasks, detail.AssignedEmployees, detail.TotalActivities, detail.Progress, documentDTOs),
		})
}

func (u *DailyChecklistController) 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.DailyChecklistService.CreateOne(c, req)
	if err != nil {
		return err
	}

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

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

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

	responseData := make([]dto.DailyChecklistListDTO, len(results))
	for i, item := range results {
		responseData[i] = dto.ToDailyChecklistListDTO(item)
	}

	return c.Status(fiber.StatusOK).
		JSON(response.Success{
			Code:    fiber.StatusOK,
			Status:  "success",
			Message: "Bulk update dailyChecklist successfully",
			Data:    responseData,
		})
}

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

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

	form, err := c.MultipartForm()
	if err != nil {
		return fiber.NewError(fiber.StatusBadRequest, "Invalid multipart form")
	}
	req.Documents = form.File["documents"]
	if err := validateDailyChecklistDocumentSizes(req.Documents); err != nil {
		return err
	}

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

	result, err := u.DailyChecklistService.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 dailyChecklist successfully",
			Data:    dto.ToDailyChecklistListDTO(*result),
		})
}

func validateDailyChecklistDocumentSizes(files []*multipart.FileHeader) error {
	const maxDailyChecklistDocumentBytes = 5 * 1024 * 1024 // 5MB
	for _, file := range files {
		if file != nil && file.Size > maxDailyChecklistDocumentBytes {
			return fiber.NewError(fiber.StatusRequestEntityTooLarge, "Document size must be <= 5MB")
		}
	}
	return nil
}

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

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

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

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

func (u *DailyChecklistController) CreateDailyChecklistPhase(c *fiber.Ctx) error {
	param := c.Params("idDailyChecklist")
	id, err := strconv.Atoi(param)
	if err != nil {
		return fiber.NewError(fiber.StatusBadRequest, "Invalid daily checklist id")
	}

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

	if err := u.DailyChecklistService.AssignPhases(c, uint(id), req); err != nil {
		return err
	}

	return c.Status(fiber.StatusCreated).
		JSON(response.Success{
			Code:    fiber.StatusCreated,
			Status:  "success",
			Message: "Daily checklist phases saved successfully",
		})
}

func (u *DailyChecklistController) CreateAssignment(c *fiber.Ctx) error {
	param := c.Params("idDailyChecklist")
	id, err := strconv.Atoi(param)
	if err != nil {
		return fiber.NewError(fiber.StatusBadRequest, "Invalid daily checklist id")
	}

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

	if err := u.DailyChecklistService.AssignTasks(c, uint(id), req); err != nil {
		return err
	}

	return c.Status(fiber.StatusCreated).
		JSON(response.Success{
			Code:    fiber.StatusCreated,
			Status:  "success",
			Message: "Daily checklist assignments saved successfully",
		})
}

func (u *DailyChecklistController) RemoveAssignment(c *fiber.Ctx) error {
	dailyChecklistParam := c.Params("idDailyChecklist")
	employeeParam := c.Params("idEmployee")

	dailyChecklistID, err := strconv.Atoi(dailyChecklistParam)
	if err != nil {
		return fiber.NewError(fiber.StatusBadRequest, "Invalid daily checklist id")
	}

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

	if err := u.DailyChecklistService.RemoveAssignment(c, uint(dailyChecklistID), uint(employeeID)); err != nil {
		return err
	}

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

func (u *DailyChecklistController) GetPhaseByIdChecklist(c *fiber.Ctx) error {
	param := c.Params("idDailyChecklist")
	id, err := strconv.Atoi(param)
	if err != nil || id <= 0 {
		return fiber.NewError(fiber.StatusBadRequest, "Invalid daily checklist id")
	}

	phaseIDs, err := u.DailyChecklistService.GetChecklistPhaseIDs(c, uint(id))
	if err != nil {
		return err
	}

	responseData := make([]map[string]uint, len(phaseIDs))
	for i, phaseID := range phaseIDs {
		responseData[i] = map[string]uint{"phase_id": phaseID}
	}

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

func (u *DailyChecklistController) GetAllTasks(c *fiber.Ctx) error {
	checklistParam := c.Query("checklist_id", "")
	if checklistParam == "" {
		return fiber.NewError(fiber.StatusBadRequest, "checklist_id is required")
	}

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

	result, err := u.DailyChecklistService.GetTasks(c, uint(checklistID))
	if err != nil {
		return err
	}

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

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

	if err := u.DailyChecklistService.UpdateAssignment(c, req); err != nil {
		return err
	}

	return c.Status(fiber.StatusOK).
		JSON(response.Success{
			Code:    fiber.StatusOK,
			Status:  "success",
			Message: "Assignment updated successfully",
		})
}
