package controller

import (
	"encoding/json"
	"fmt"
	"math"
	"strconv"
	"strings"
	"time"

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

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

type ProjectflockController struct {
	ProjectflockService service.ProjectflockService
}

func NewProjectflockController(projectflockService service.ProjectflockService) *ProjectflockController {
	return &ProjectflockController{
		ProjectflockService: projectflockService,
	}
}

func (u *ProjectflockController) GetAll(c *fiber.Ctx) error {
	parseUintList := func(raw string) ([]uint, error) {
		raw = strings.TrimSpace(raw)
		if raw == "" {
			return nil, nil
		}

		var ids []uint
		if strings.HasPrefix(raw, "[") {
			if err := json.Unmarshal([]byte(raw), &ids); err == nil {
				return ids, nil
			}
		}

		parts := strings.Split(raw, ",")
		for _, part := range parts {
			part = strings.Trim(part, " \"[]")
			if part == "" {
				continue
			}
			v, err := strconv.Atoi(part)
			if err != nil || v <= 0 {
				return nil, fmt.Errorf("invalid kandang id: %s", part)
			}
			ids = append(ids, uint(v))
		}
		return ids, nil
	}

	query := &validation.Query{
		Page:      c.QueryInt("page", 1),
		Limit:     c.QueryInt("limit", 10),
		Search:    c.Query("search", ""),
		SortBy:    c.Query("sort_by", ""),
		SortOrder: c.Query("sort_order", ""),
		Status:    strings.TrimSpace(c.Query("status", "")),
	}

	if area := c.QueryInt("area_id", 0); area > 0 {
		query.AreaId = uint(area)
	}
	if location := c.QueryInt("location_id", 0); location > 0 {
		query.LocationId = uint(location)
	}
	if period := c.QueryInt("period", 0); period > 0 {
		query.Period = period
	}
	if category := c.Query("category", ""); category != "" {
		query.Category = category

	}
	query.TransferContext = c.Query(utils.TransferContextKey, "")

	if kandangRaw := c.Query("kandang_id", c.Query("kandang_ids", "")); kandangRaw != "" {
		ids, err := parseUintList(kandangRaw)
		if err != nil {
			return fiber.NewError(fiber.StatusBadRequest, err.Error())
		}
		query.KandangIds = ids
	}

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

	var periodMap map[uint]int
	if len(result) > 0 {
		ids := make([]uint, len(result))
		for i, item := range result {
			ids[i] = item.Id
		}
		if periods, err := u.ProjectflockService.GetProjectPeriods(c, ids); err == nil {
			periodMap = periods
		}
	}

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

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

	var period int
	if periods, err := u.ProjectflockService.GetProjectPeriods(c, []uint{uint(id)}); err == nil {
		if p, ok := periods[uint(id)]; ok {
			period = p
		}
	}

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

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

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

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

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

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

	var (
		data    interface{}
		message = "Submit projectflock approval successfully"
	)

	var periodMap map[uint]int
	if len(results) > 0 {
		ids := make([]uint, len(results))
		for i, item := range results {
			ids[i] = item.Id
		}
		if periods, err := u.ProjectflockService.GetProjectPeriods(c, ids); err == nil {
			periodMap = periods
		}
	}

	if len(results) == 1 {
		period := 0
		if periodMap != nil {
			if p, ok := periodMap[results[0].Id]; ok {
				period = p
			}
		}
		data = dto.ToProjectFlockListDTOWithPeriod(results[0], period)
	} else {
		message = "Submit projectflock approvals successfully"
		data = dto.ToProjectFlockListDTOsWithPeriods(results, periodMap)
	}

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

func (u *ProjectflockController) GetPeriodSummary(c *fiber.Ctx) error {
	param := c.Params("location_id")

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

	summaries, err := u.ProjectflockService.GetPeriodSummary(c, uint(id))
	if err != nil {
		return err
	}

	responseBody := make([]dto.KandangPeriodSummaryDTO, 0, len(summaries))
	for _, item := range summaries {
		responseBody = append(responseBody, dto.KandangPeriodSummaryDTO{
			Id:     item.Id,
			Name:   item.Name,
			Period: item.Period,
		})
	}

	return c.Status(fiber.StatusOK).
		JSON(response.Success{
			Code:    fiber.StatusOK,
			Status:  "success",
			Message: "Get kandang period summary successfully",
			Data:    responseBody,
		})
}

func (u *ProjectflockController) LookupProjectFlockKandang(c *fiber.Ctx) error {
	projectFlockId := c.QueryInt("project_flock_id", 0)
	kandangId := c.QueryInt("kandang_id", 0)
	withPopulation := c.QueryBool("withpopulation", false)
	recordDateRaw := strings.TrimSpace(c.Query("record_date", ""))
	var recordDate *time.Time

	if projectFlockId == 0 || kandangId == 0 {
		return fiber.NewError(fiber.StatusBadRequest, "Invalid project_flock_id or kandang_id")
	}
	if recordDateRaw != "" {
		parsed, err := time.Parse("2006-01-02", recordDateRaw)
		if err != nil {
			return fiber.NewError(fiber.StatusBadRequest, "record_date must be in YYYY-MM-DD format")
		}
		utc := parsed.UTC()
		recordDate = &utc
	}

	result, availableStock, err := u.ProjectflockService.GetProjectFlockKandangByProjectAndKandang(c, uint(projectFlockId), uint(kandangId))
	if err != nil {
		return err
	}
	_ = availableStock

	dtoResult := dto.ToProjectFlockKandangDTO(*result)
	if population, err := u.ProjectflockService.GetProjectFlockKandangPopulation(c, result.Id); err != nil {
		return err
	} else {
		dtoResult.AvailableQuantity = population
	}
	if chickinDate, err := u.ProjectflockService.GetProjectFlockKandangChickinDate(c, result.Id); err != nil {
		return err
	} else if chickinDate != nil {
		dtoResult.ChickInDate = chickinDate
	}
	if warehouse, werr := u.ProjectflockService.GetWarehouseByKandangID(c, result.KandangId); werr != nil {
		return werr
	} else if warehouse != nil {
		mapped := warehouseDTO.ToWarehouseRelationDTO(*warehouse)
		dtoResult.Warehouse = &mapped
	}
	if _, isLaying, serr := u.ProjectflockService.GetProjectFlockKandangTransferStateAtDate(c, result.Id, recordDate); serr != nil {
		return serr
	} else {
		dtoResult.IsTransition = false
		dtoResult.IsLaying = isLaying
	}
	applyCutOverLayingLookupOverride(&dtoResult)
	if withPopulation {
		population := dtoResult.AvailableQuantity
		dtoResult.Population = &population
	}

	if dtoResult.ProjectFlock != nil {
		for i := range dtoResult.ProjectFlock.Kandangs {
			kand := &dtoResult.ProjectFlock.Kandangs[i]
			if kand.Id == 0 {
				continue
			}
			if q, qerr := u.ProjectflockService.GetAvailableDocQuantity(c, kand.Id); qerr == nil {
				kand.AvailableQuantity = q
			}
		}

		dtoResult.ProjectFlock.Kandangs = nil
	}

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

func applyCutOverLayingLookupOverride(result *dto.ProjectFlockKandangDTO) {
	if result == nil || result.ProjectFlock == nil || result.IsLaying || result.ChickInDate == nil {
		return
	}

	category := strings.ToUpper(strings.TrimSpace(result.ProjectFlock.Category))
	if category != strings.ToUpper(string(utils.ProjectFlockCategoryLaying)) {
		return
	}

	result.IsTransition = false
	result.IsLaying = true
}

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

	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.ProjectflockService.Resubmit(c, req, uint(id))
	if err != nil {
		return err
	}

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