package handler

import (
	"apskel-pos-be/internal/appcontext"
	"apskel-pos-be/internal/constants"
	"apskel-pos-be/internal/contract"
	"apskel-pos-be/internal/logger"
	"apskel-pos-be/internal/models"
	"apskel-pos-be/internal/service"
	"apskel-pos-be/internal/util"
	"net/http"

	"github.com/gin-gonic/gin"
	"github.com/google/uuid"
)

type ProductRecipeHandler struct {
	productRecipeService service.ProductRecipeService
}

func NewProductRecipeHandler(productRecipeService service.ProductRecipeService) *ProductRecipeHandler {
	return &ProductRecipeHandler{
		productRecipeService: productRecipeService,
	}
}

func (h *ProductRecipeHandler) Create(c *gin.Context) {
	ctx := c.Request.Context()
	contextInfo := appcontext.FromGinContext(ctx)

	var request models.CreateProductRecipeRequest
	if err := c.ShouldBindJSON(&request); err != nil {
		logger.FromContext(ctx).WithError(err).Error("ProductRecipeHandler::Create -> request binding failed")
		validationResponseError := contract.NewResponseError(constants.MissingFieldErrorCode, constants.RequestEntity, err.Error())
		util.HandleResponse(c.Writer, c.Request, contract.BuildErrorResponse([]*contract.ResponseError{validationResponseError}), "ProductRecipeHandler::Create")
		return
	}

	recipeResponse, err := h.productRecipeService.Create(ctx, contextInfo.OrganizationID, &request)
	if err != nil {
		logger.FromContext(ctx).WithError(err).Error("ProductRecipeHandler::Create -> Failed to create product recipe")
		validationResponseError := contract.NewResponseError(constants.InternalServerErrorCode, constants.RequestEntity, err.Error())
		util.HandleResponse(c.Writer, c.Request, contract.BuildErrorResponse([]*contract.ResponseError{validationResponseError}), "ProductRecipeHandler::Create")
		return
	}

	util.HandleResponse(c.Writer, c.Request, contract.BuildSuccessResponse(recipeResponse), "ProductRecipeHandler::Create")
}

func (h *ProductRecipeHandler) GetByID(c *gin.Context) {
	ctx := c.Request.Context()
	contextInfo := appcontext.FromGinContext(ctx)

	idStr := c.Param("id")
	id, err := uuid.Parse(idStr)
	if err != nil {
		logger.FromContext(ctx).WithError(err).Error("ProductRecipeHandler::GetByID -> Invalid recipe ID")
		validationResponseError := contract.NewResponseError(constants.MalformedFieldErrorCode, constants.RequestEntity, "Invalid recipe ID")
		util.HandleResponse(c.Writer, c.Request, contract.BuildErrorResponse([]*contract.ResponseError{validationResponseError}), "ProductRecipeHandler::GetByID")
		return
	}

	recipeResponse, err := h.productRecipeService.GetByID(ctx, id, contextInfo.OrganizationID)
	if err != nil {
		logger.FromContext(ctx).WithError(err).Error("ProductRecipeHandler::GetByID -> Failed to get product recipe")
		validationResponseError := contract.NewResponseError(constants.NotFoundErrorCode, constants.RequestEntity, "Product recipe not found")
		util.HandleResponse(c.Writer, c.Request, contract.BuildErrorResponse([]*contract.ResponseError{validationResponseError}), "ProductRecipeHandler::GetByID")
		return
	}

	util.HandleResponse(c.Writer, c.Request, contract.BuildSuccessResponse(recipeResponse), "ProductRecipeHandler::GetByID")
}

func (h *ProductRecipeHandler) GetByProductID(c *gin.Context) {
	ctx := c.Request.Context()
	contextInfo := appcontext.FromGinContext(ctx)

	productIDStr := c.Param("product_id")
	productID, err := uuid.Parse(productIDStr)
	if err != nil {
		logger.FromContext(ctx).WithError(err).Error("ProductRecipeHandler::GetByProductID -> Invalid product ID")
		validationResponseError := contract.NewResponseError(constants.MalformedFieldErrorCode, constants.RequestEntity, "Invalid product ID")
		util.HandleResponse(c.Writer, c.Request, contract.BuildErrorResponse([]*contract.ResponseError{validationResponseError}), "ProductRecipeHandler::GetByProductID")
		return
	}

	// Check if variant_id is provided
	variantIDStr := c.Query("variant_id")
	var variantID *uuid.UUID
	if variantIDStr != "" {
		parsed, err := uuid.Parse(variantIDStr)
		if err != nil {
			logger.FromContext(ctx).WithError(err).Error("ProductRecipeHandler::GetByProductID -> Invalid variant ID")
			validationResponseError := contract.NewResponseError(constants.MalformedFieldErrorCode, constants.RequestEntity, "Invalid variant ID")
			util.HandleResponse(c.Writer, c.Request, contract.BuildErrorResponse([]*contract.ResponseError{validationResponseError}), "ProductRecipeHandler::GetByProductID")
			return
		}
		variantID = &parsed
	}

	var recipes []*models.ProductRecipeResponse
	if variantIDStr != "" {
		// Get by product and variant ID
		recipes, err = h.productRecipeService.GetByProductAndVariantID(ctx, productID, variantID, contextInfo.OrganizationID)
	} else {
		// Get by product ID only
		recipes, err = h.productRecipeService.GetByProductID(ctx, productID, contextInfo.OrganizationID)
	}

	if err != nil {
		logger.FromContext(ctx).WithError(err).Error("ProductRecipeHandler::GetByProductID -> Failed to get product recipes")
		validationResponseError := contract.NewResponseError(constants.InternalServerErrorCode, constants.RequestEntity, err.Error())
		util.HandleResponse(c.Writer, c.Request, contract.BuildErrorResponse([]*contract.ResponseError{validationResponseError}), "ProductRecipeHandler::GetByProductID")
		return
	}

	util.HandleResponse(c.Writer, c.Request, contract.BuildSuccessResponse(recipes), "ProductRecipeHandler::GetByProductID")
}

func (h *ProductRecipeHandler) GetByIngredientID(c *gin.Context) {
	ctx := c.Request.Context()
	contextInfo := appcontext.FromGinContext(ctx)

	ingredientIDStr := c.Param("ingredient_id")
	ingredientID, err := uuid.Parse(ingredientIDStr)
	if err != nil {
		logger.FromContext(ctx).WithError(err).Error("ProductRecipeHandler::GetByIngredientID -> Invalid ingredient ID")
		validationResponseError := contract.NewResponseError(constants.MalformedFieldErrorCode, constants.RequestEntity, "Invalid ingredient ID")
		util.HandleResponse(c.Writer, c.Request, contract.BuildErrorResponse([]*contract.ResponseError{validationResponseError}), "ProductRecipeHandler::GetByIngredientID")
		return
	}

	recipes, err := h.productRecipeService.GetByIngredientID(ctx, ingredientID, contextInfo.OrganizationID)
	if err != nil {
		logger.FromContext(ctx).WithError(err).Error("ProductRecipeHandler::GetByIngredientID -> Failed to get product recipes")
		validationResponseError := contract.NewResponseError(constants.InternalServerErrorCode, constants.RequestEntity, err.Error())
		util.HandleResponse(c.Writer, c.Request, contract.BuildErrorResponse([]*contract.ResponseError{validationResponseError}), "ProductRecipeHandler::GetByIngredientID")
		return
	}

	util.HandleResponse(c.Writer, c.Request, contract.BuildSuccessResponse(recipes), "ProductRecipeHandler::GetByIngredientID")
}

func (h *ProductRecipeHandler) Update(c *gin.Context) {
	ctx := c.Request.Context()
	contextInfo := appcontext.FromGinContext(ctx)

	idStr := c.Param("id")
	id, err := uuid.Parse(idStr)
	if err != nil {
		logger.FromContext(ctx).WithError(err).Error("ProductRecipeHandler::Update -> Invalid recipe ID")
		validationResponseError := contract.NewResponseError(constants.MalformedFieldErrorCode, constants.RequestEntity, "Invalid recipe ID")
		util.HandleResponse(c.Writer, c.Request, contract.BuildErrorResponse([]*contract.ResponseError{validationResponseError}), "ProductRecipeHandler::Update")
		return
	}

	var request models.UpdateProductRecipeRequest
	if err := c.ShouldBindJSON(&request); err != nil {
		logger.FromContext(ctx).WithError(err).Error("ProductRecipeHandler::Update -> request binding failed")
		validationResponseError := contract.NewResponseError(constants.MissingFieldErrorCode, constants.RequestEntity, err.Error())
		util.HandleResponse(c.Writer, c.Request, contract.BuildErrorResponse([]*contract.ResponseError{validationResponseError}), "ProductRecipeHandler::Update")
		return
	}

	recipeResponse, err := h.productRecipeService.Update(ctx, id, contextInfo.OrganizationID, &request)
	if err != nil {
		logger.FromContext(ctx).WithError(err).Error("ProductRecipeHandler::Update -> Failed to update product recipe")
		validationResponseError := contract.NewResponseError(constants.InternalServerErrorCode, constants.RequestEntity, err.Error())
		util.HandleResponse(c.Writer, c.Request, contract.BuildErrorResponse([]*contract.ResponseError{validationResponseError}), "ProductRecipeHandler::Update")
		return
	}

	util.HandleResponse(c.Writer, c.Request, contract.BuildSuccessResponse(recipeResponse), "ProductRecipeHandler::Update")
}

func (h *ProductRecipeHandler) Delete(c *gin.Context) {
	ctx := c.Request.Context()
	contextInfo := appcontext.FromGinContext(ctx)

	idStr := c.Param("id")
	id, err := uuid.Parse(idStr)
	if err != nil {
		logger.FromContext(ctx).WithError(err).Error("ProductRecipeHandler::Delete -> Invalid recipe ID")
		validationResponseError := contract.NewResponseError(constants.MalformedFieldErrorCode, constants.RequestEntity, "Invalid recipe ID")
		util.HandleResponse(c.Writer, c.Request, contract.BuildErrorResponse([]*contract.ResponseError{validationResponseError}), "ProductRecipeHandler::Delete")
		return
	}

	err = h.productRecipeService.Delete(ctx, id, contextInfo.OrganizationID)
	if err != nil {
		logger.FromContext(ctx).WithError(err).Error("ProductRecipeHandler::Delete -> Failed to delete product recipe")
		validationResponseError := contract.NewResponseError(constants.InternalServerErrorCode, constants.RequestEntity, err.Error())
		util.HandleResponse(c.Writer, c.Request, contract.BuildErrorResponse([]*contract.ResponseError{validationResponseError}), "ProductRecipeHandler::Delete")
		return
	}

	response := map[string]interface{}{
		"message": "Product recipe deleted successfully",
	}
	util.HandleResponse(c.Writer, c.Request, contract.BuildSuccessResponse(response), "ProductRecipeHandler::Delete")
}

func (h *ProductRecipeHandler) BulkCreate(c *gin.Context) {
	ctx := c.Request.Context()
	contextInfo := appcontext.FromGinContext(ctx)

	var request struct {
		Recipes []models.CreateProductRecipeRequest `json:"recipes" validate:"required,min=1"`
	}

	if err := c.ShouldBindJSON(&request); err != nil {
		logger.FromContext(ctx).WithError(err).Error("ProductRecipeHandler::BulkCreate -> request binding failed")
		validationResponseError := contract.NewResponseError(constants.MissingFieldErrorCode, constants.RequestEntity, err.Error())
		util.HandleResponse(c.Writer, c.Request, contract.BuildErrorResponse([]*contract.ResponseError{validationResponseError}), "ProductRecipeHandler::BulkCreate")
		return
	}

	recipes, err := h.productRecipeService.BulkCreate(ctx, contextInfo.OrganizationID, request.Recipes)
	if err != nil {
		logger.FromContext(ctx).WithError(err).Error("ProductRecipeHandler::BulkCreate -> Failed to bulk create product recipes")
		validationResponseError := contract.NewResponseError(constants.InternalServerErrorCode, constants.RequestEntity, err.Error())
		util.HandleResponse(c.Writer, c.Request, contract.BuildErrorResponse([]*contract.ResponseError{validationResponseError}), "ProductRecipeHandler::BulkCreate")
		return
	}

	c.JSON(http.StatusCreated, contract.BuildSuccessResponse(recipes))
}