package fifo_stock_v2

import (
	"context"
	"errors"
	"math"
	"sort"

	commonRepo "gitlab.com/mbugroup/lti-api.git/internal/common/repository"
	entity "gitlab.com/mbugroup/lti-api.git/internal/entities"
	"gitlab.com/mbugroup/lti-api.git/internal/utils/fifo"

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

func ReleasePopulationConsumptionByUsable(
	ctx context.Context,
	tx *gorm.DB,
	usableType string,
	usableID uint,
) error {
	if tx == nil {
		return errors.New("transaction is required")
	}
	if usableType == "" || usableID == 0 {
		return errors.New("usable type and id are required")
	}

	stockAllocationRepo := commonRepo.NewStockAllocationRepository(tx)
	allocations, err := stockAllocationRepo.FindActiveByUsable(ctx, usableType, usableID, nil)
	if err != nil {
		return err
	}

	for _, allocation := range allocations {
		if allocation.StockableType != fifo.StockableKeyProjectFlockPopulation.String() || allocation.StockableId == 0 || allocation.Qty <= 0 {
			continue
		}
		if err := tx.WithContext(ctx).
			Model(&entity.ProjectFlockPopulation{}).
			Where("id = ?", allocation.StockableId).
			Update("total_used_qty", gorm.Expr("GREATEST(total_used_qty - ?, 0)", allocation.Qty)).Error; err != nil {
			return err
		}
	}

	return stockAllocationRepo.ReleaseByUsable(ctx, usableType, usableID, nil, nil)
}

func AllocatePopulationConsumption(
	ctx context.Context,
	tx *gorm.DB,
	populations []entity.ProjectFlockPopulation,
	productWarehouseID uint,
	usableType string,
	usableID uint,
	consumeQty float64,
) error {
	if consumeQty <= 0 {
		return nil
	}
	if tx == nil {
		return errors.New("transaction is required")
	}
	if productWarehouseID == 0 {
		return fiber.NewError(fiber.StatusBadRequest, "Product warehouse tidak valid")
	}
	if usableType == "" || usableID == 0 {
		return errors.New("usable type and id are required")
	}
	if len(populations) == 0 {
		return fiber.NewError(fiber.StatusBadRequest, "Populasi tidak ditemukan")
	}

	if err := ReleasePopulationConsumptionByUsable(ctx, tx, usableType, usableID); err != nil {
		return err
	}

	sort.Slice(populations, func(i, j int) bool {
		if populations[i].CreatedAt.Equal(populations[j].CreatedAt) {
			return populations[i].Id < populations[j].Id
		}
		return populations[i].CreatedAt.Before(populations[j].CreatedAt)
	})

	stockAllocationRepo := commonRepo.NewStockAllocationRepository(tx)
	remaining := consumeQty
	for _, pop := range populations {
		available := pop.TotalQty - pop.TotalUsedQty
		if available <= 0 {
			continue
		}
		portion := math.Min(available, remaining)
		if portion <= 0 {
			continue
		}

		allocation := &entity.StockAllocation{
			ProductWarehouseId: productWarehouseID,
			StockableType:      fifo.StockableKeyProjectFlockPopulation.String(),
			StockableId:        pop.Id,
			UsableType:         usableType,
			UsableId:           usableID,
			Qty:                portion,
			Status:             entity.StockAllocationStatusActive,
			AllocationPurpose:  entity.StockAllocationPurposeConsume,
		}
		if err := stockAllocationRepo.CreateOne(ctx, allocation, nil); err != nil {
			return err
		}

		if err := tx.WithContext(ctx).
			Model(&entity.ProjectFlockPopulation{}).
			Where("id = ?", pop.Id).
			Update("total_used_qty", gorm.Expr("total_used_qty + ?", portion)).Error; err != nil {
			return err
		}

		remaining -= portion
		if remaining <= 1e-6 {
			break
		}
	}

	if remaining > 1e-6 {
		return fiber.NewError(fiber.StatusBadRequest, "Populasi tidak mencukupi")
	}

	return nil
}
