package validator

import (
	"apskel-pos-be/internal/models"
	"fmt"
	"strings"

	"github.com/go-playground/validator/v10"
)

type OrderIngredientTransactionValidator interface {
	ValidateCreateOrderIngredientTransactionRequest(req *models.CreateOrderIngredientTransactionRequest) error
	ValidateUpdateOrderIngredientTransactionRequest(req *models.UpdateOrderIngredientTransactionRequest) error
	ValidateListOrderIngredientTransactionsRequest(req *models.ListOrderIngredientTransactionsRequest) error
}

type OrderIngredientTransactionValidatorImpl struct {
	validator *validator.Validate
}

func NewOrderIngredientTransactionValidator() OrderIngredientTransactionValidator {
	v := validator.New()
	return &OrderIngredientTransactionValidatorImpl{
		validator: v,
	}
}

func (v *OrderIngredientTransactionValidatorImpl) ValidateCreateOrderIngredientTransactionRequest(req *models.CreateOrderIngredientTransactionRequest) error {
	if err := v.validator.Struct(req); err != nil {
		var validationErrors []string
		for _, err := range err.(validator.ValidationErrors) {
			validationErrors = append(validationErrors, fmt.Sprintf("%s: %s", err.Field(), getValidationMessage(err)))
		}
		return fmt.Errorf("validation failed: %s", strings.Join(validationErrors, ", "))
	}

	// Custom validations
	if req.GrossQty < req.NetQty {
		return fmt.Errorf("gross quantity must be greater than or equal to net quantity")
	}

	expectedWasteQty := req.GrossQty - req.NetQty
	if req.WasteQty != expectedWasteQty {
		return fmt.Errorf("waste quantity must equal gross quantity minus net quantity")
	}

	return nil
}

func (v *OrderIngredientTransactionValidatorImpl) ValidateUpdateOrderIngredientTransactionRequest(req *models.UpdateOrderIngredientTransactionRequest) error {
	if err := v.validator.Struct(req); err != nil {
		var validationErrors []string
		for _, err := range err.(validator.ValidationErrors) {
			validationErrors = append(validationErrors, fmt.Sprintf("%s: %s", err.Field(), getValidationMessage(err)))
		}
		return fmt.Errorf("validation failed: %s", strings.Join(validationErrors, ", "))
	}

	// Custom validations for partial updates
	if req.GrossQty != nil && req.NetQty != nil {
		if *req.GrossQty < *req.NetQty {
			return fmt.Errorf("gross quantity must be greater than or equal to net quantity")
		}
	}

	if req.GrossQty != nil && req.NetQty != nil && req.WasteQty != nil {
		expectedWasteQty := *req.GrossQty - *req.NetQty
		if *req.WasteQty != expectedWasteQty {
			return fmt.Errorf("waste quantity must equal gross quantity minus net quantity")
		}
	}

	return nil
}

func (v *OrderIngredientTransactionValidatorImpl) ValidateListOrderIngredientTransactionsRequest(req *models.ListOrderIngredientTransactionsRequest) error {
	if err := v.validator.Struct(req); err != nil {
		var validationErrors []string
		for _, err := range err.(validator.ValidationErrors) {
			validationErrors = append(validationErrors, fmt.Sprintf("%s: %s", err.Field(), getValidationMessage(err)))
		}
		return fmt.Errorf("validation failed: %s", strings.Join(validationErrors, ", "))
	}

	// Custom validations
	if req.StartDate != nil && req.EndDate != nil {
		if req.StartDate.After(*req.EndDate) {
			return fmt.Errorf("start date must be before end date")
		}
	}

	return nil
}

func getValidationMessage(err validator.FieldError) string {
	switch err.Tag() {
	case "required":
		return "is required"
	case "min":
		return fmt.Sprintf("must be at least %s", err.Param())
	case "max":
		return fmt.Sprintf("must be at most %s", err.Param())
	case "gt":
		return fmt.Sprintf("must be greater than %s", err.Param())
	case "gte":
		return fmt.Sprintf("must be greater than or equal to %s", err.Param())
	case "lt":
		return fmt.Sprintf("must be less than %s", err.Param())
	case "lte":
		return fmt.Sprintf("must be less than or equal to %s", err.Param())
	case "email":
		return "must be a valid email address"
	case "uuid":
		return "must be a valid UUID"
	case "len":
		return fmt.Sprintf("must be exactly %s characters long", err.Param())
	default:
		return "is invalid"
	}
}
