package handler

import (
	"strconv"
	"time"

	"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"
	"apskel-pos-be/internal/validator"

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

type InventoryHandler struct {
	inventoryService   service.InventoryService
	inventoryValidator validator.InventoryValidator
}

func NewInventoryHandler(
	inventoryService service.InventoryService,
	inventoryValidator validator.InventoryValidator,
) *InventoryHandler {
	return &InventoryHandler{
		inventoryService:   inventoryService,
		inventoryValidator: inventoryValidator,
	}
}

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

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

	validationError, validationErrorCode := h.inventoryValidator.ValidateCreateInventoryRequest(&req)
	if validationError != nil {
		validationResponseError := contract.NewResponseError(validationErrorCode, constants.RequestEntity, validationError.Error())
		util.HandleResponse(c.Writer, c.Request, contract.BuildErrorResponse([]*contract.ResponseError{validationResponseError}), "InventoryHandler::CreateInventory")
		return
	}

	inventoryResponse := h.inventoryService.CreateInventory(ctx, contextInfo, &req)
	if inventoryResponse.HasErrors() {
		errorResp := inventoryResponse.GetErrors()[0]
		logger.FromContext(ctx).WithError(errorResp).Error("InventoryHandler::CreateInventory -> Failed to create inventory from service")
	}

	util.HandleResponse(c.Writer, c.Request, inventoryResponse, "InventoryHandler::CreateInventory")
}

func (h *InventoryHandler) UpdateInventory(c *gin.Context) {
	ctx := c.Request.Context()

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

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

	validationError, validationErrorCode := h.inventoryValidator.ValidateUpdateInventoryRequest(&req)
	if validationError != nil {
		validationResponseError := contract.NewResponseError(validationErrorCode, constants.RequestEntity, validationError.Error())
		util.HandleResponse(c.Writer, c.Request, contract.BuildErrorResponse([]*contract.ResponseError{validationResponseError}), "InventoryHandler::UpdateInventory")
		return
	}

	inventoryResponse := h.inventoryService.UpdateInventory(ctx, inventoryID, &req)
	if inventoryResponse.HasErrors() {
		errorResp := inventoryResponse.GetErrors()[0]
		logger.FromContext(ctx).WithError(errorResp).Error("InventoryHandler::UpdateInventory -> Failed to update inventory from service")
	}

	util.HandleResponse(c.Writer, c.Request, inventoryResponse, "InventoryHandler::UpdateInventory")
}

func (h *InventoryHandler) DeleteInventory(c *gin.Context) {
	ctx := c.Request.Context()

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

	inventoryResponse := h.inventoryService.DeleteInventory(ctx, inventoryID)
	if inventoryResponse.HasErrors() {
		errorResp := inventoryResponse.GetErrors()[0]
		logger.FromContext(ctx).WithError(errorResp).Error("InventoryHandler::DeleteInventory -> Failed to delete inventory from service")
	}

	util.HandleResponse(c.Writer, c.Request, inventoryResponse, "InventoryHandler::DeleteInventory")
}

func (h *InventoryHandler) GetInventory(c *gin.Context) {
	ctx := c.Request.Context()

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

	inventoryResponse := h.inventoryService.GetInventoryByID(ctx, inventoryID)
	if inventoryResponse.HasErrors() {
		errorResp := inventoryResponse.GetErrors()[0]
		logger.FromContext(ctx).WithError(errorResp).Error("InventoryHandler::GetInventory -> Failed to get inventory from service")
	}

	util.HandleResponse(c.Writer, c.Request, inventoryResponse, "InventoryHandler::GetInventory")
}

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

	req := &contract.ListInventoryRequest{
		Page:     1,
		Limit:    10,
		OutletID: &contextInfo.OutletID,
	}

	if pageStr := c.Query("page"); pageStr != "" {
		if page, err := strconv.Atoi(pageStr); err == nil {
			req.Page = page
		}
	}

	if limitStr := c.Query("limit"); limitStr != "" {
		if limit, err := strconv.Atoi(limitStr); err == nil {
			req.Limit = limit
		}
	}

	if search := c.Query("search"); search != "" {
		req.Search = search
	}

	if outletIDStr := c.Query("outlet_id"); outletIDStr != "" {
		if outletID, err := uuid.Parse(outletIDStr); err == nil {
			req.OutletID = &outletID
		}
	}

	if productIDStr := c.Query("product_id"); productIDStr != "" {
		if productID, err := uuid.Parse(productIDStr); err == nil {
			req.ProductID = &productID
		}
	}

	if categoryIDStr := c.Query("category_id"); categoryIDStr != "" {
		if categoryID, err := uuid.Parse(categoryIDStr); err == nil {
			req.CategoryID = &categoryID
		}
	}

	if lowStockStr := c.Query("low_stock_only"); lowStockStr != "" {
		if lowStock, err := strconv.ParseBool(lowStockStr); err == nil {
			req.LowStockOnly = &lowStock
		}
	}

	if zeroStockStr := c.Query("zero_stock_only"); zeroStockStr != "" {
		if zeroStock, err := strconv.ParseBool(zeroStockStr); err == nil {
			req.ZeroStockOnly = &zeroStock
		}
	}

	validationError, validationErrorCode := h.inventoryValidator.ValidateListInventoryRequest(req)
	if validationError != nil {
		logger.FromContext(ctx).WithError(validationError).Error("InventoryHandler::ListInventory -> request validation failed")
		validationResponseError := contract.NewResponseError(validationErrorCode, constants.RequestEntity, validationError.Error())
		util.HandleResponse(c.Writer, c.Request, contract.BuildErrorResponse([]*contract.ResponseError{validationResponseError}), "InventoryHandler::ListInventory")
		return
	}

	inventoryResponse := h.inventoryService.ListInventory(ctx, req)
	if inventoryResponse.HasErrors() {
		errorResp := inventoryResponse.GetErrors()[0]
		logger.FromContext(ctx).WithError(errorResp).Error("InventoryHandler::ListInventory -> Failed to list inventory from service")
	}

	util.HandleResponse(c.Writer, c.Request, inventoryResponse, "InventoryHandler::ListInventory")
}

func (h *InventoryHandler) AdjustInventory(c *gin.Context) {
	ctx := c.Request.Context()

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

	validationError, validationErrorCode := h.inventoryValidator.ValidateAdjustInventoryRequest(&req)
	if validationError != nil {
		validationResponseError := contract.NewResponseError(validationErrorCode, constants.RequestEntity, validationError.Error())
		util.HandleResponse(c.Writer, c.Request, contract.BuildErrorResponse([]*contract.ResponseError{validationResponseError}), "InventoryHandler::AdjustInventory")
		return
	}

	inventoryResponse := h.inventoryService.AdjustInventory(ctx, &req)
	if inventoryResponse.HasErrors() {
		errorResp := inventoryResponse.GetErrors()[0]
		logger.FromContext(ctx).WithError(errorResp).Error("InventoryHandler::AdjustInventory -> Failed to adjust inventory from service")
	}

	util.HandleResponse(c.Writer, c.Request, inventoryResponse, "InventoryHandler::AdjustInventory")
}

func (h *InventoryHandler) RestockInventory(c *gin.Context) {
	ctx := c.Request.Context()

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

	// TODO: Add validation for restock request
	// validationError, validationErrorCode := h.inventoryValidator.ValidateRestockInventoryRequest(&req)
	// if validationError != nil {
	// 	validationResponseError := contract.NewResponseError(validationErrorCode, constants.RequestEntity, validationError.Error())
	// 	util.HandleResponse(c.Writer, c.Request, contract.BuildErrorResponse([]*contract.ResponseError{validationResponseError}), "InventoryHandler::RestockInventory")
	// 	return
	// }

	inventoryResponse := h.inventoryService.RestockInventory(ctx, &req)
	if inventoryResponse.HasErrors() {
		errorResp := inventoryResponse.GetErrors()[0]
		logger.FromContext(ctx).WithError(errorResp).Error("InventoryHandler::RestockInventory -> Failed to restock inventory from service")
	}

	util.HandleResponse(c.Writer, c.Request, inventoryResponse, "InventoryHandler::RestockInventory")
}

func (h *InventoryHandler) GetLowStockItems(c *gin.Context) {
	ctx := c.Request.Context()

	outletIDStr := c.Param("outlet_id")
	outletID, err := uuid.Parse(outletIDStr)
	if err != nil {
		logger.FromContext(ctx).WithError(err).Error("InventoryHandler::GetLowStockItems -> Invalid outlet ID")
		validationResponseError := contract.NewResponseError(constants.MalformedFieldErrorCode, constants.RequestEntity, "Invalid outlet ID")
		util.HandleResponse(c.Writer, c.Request, contract.BuildErrorResponse([]*contract.ResponseError{validationResponseError}), "InventoryHandler::GetLowStockItems")
		return
	}

	inventoryResponse := h.inventoryService.GetLowStockItems(ctx, outletID)
	if inventoryResponse.HasErrors() {
		errorResp := inventoryResponse.GetErrors()[0]
		logger.FromContext(ctx).WithError(errorResp).Error("InventoryHandler::GetLowStockItems -> Failed to get low stock items from service")
	}

	util.HandleResponse(c.Writer, c.Request, inventoryResponse, "InventoryHandler::GetLowStockItems")
}

func (h *InventoryHandler) GetZeroStockItems(c *gin.Context) {
	ctx := c.Request.Context()

	outletIDStr := c.Param("outlet_id")
	outletID, err := uuid.Parse(outletIDStr)
	if err != nil {
		logger.FromContext(ctx).WithError(err).Error("InventoryHandler::GetZeroStockItems -> Invalid outlet ID")
		validationResponseError := contract.NewResponseError(constants.MalformedFieldErrorCode, constants.RequestEntity, "Invalid outlet ID")
		util.HandleResponse(c.Writer, c.Request, contract.BuildErrorResponse([]*contract.ResponseError{validationResponseError}), "InventoryHandler::GetZeroStockItems")
		return
	}

	inventoryResponse := h.inventoryService.GetZeroStockItems(ctx, outletID)
	if inventoryResponse.HasErrors() {
		errorResp := inventoryResponse.GetErrors()[0]
		logger.FromContext(ctx).WithError(errorResp).Error("InventoryHandler::GetZeroStockItems -> Failed to get zero stock items from service")
	}

	util.HandleResponse(c.Writer, c.Request, inventoryResponse, "InventoryHandler::GetZeroStockItems")
}

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

	outletIDStr := c.Param("outlet_id")
	outletID, err := uuid.Parse(outletIDStr)
	if err != nil {
		logger.FromContext(ctx).WithError(err).Error("InventoryHandler::GetInventoryReportSummary -> Invalid outlet ID")
		validationResponseError := contract.NewResponseError(constants.MalformedFieldErrorCode, constants.RequestEntity, "Invalid outlet ID")
		util.HandleResponse(c.Writer, c.Request, contract.BuildErrorResponse([]*contract.ResponseError{validationResponseError}), "InventoryHandler::GetInventoryReportSummary")
		return
	}

	// Parse date range parameters for summary
	var dateFrom, dateTo *time.Time
	if dateFromStr := c.Query("date_from"); dateFromStr != "" {
		if parsedDateFrom, err := time.Parse("2006-01-02", dateFromStr); err == nil {
			dateFrom = &parsedDateFrom
		}
	}
	if dateToStr := c.Query("date_to"); dateToStr != "" {
		if parsedDateTo, err := time.Parse("2006-01-02", dateToStr); err == nil {
			dateTo = &parsedDateTo
		}
	}

	summary, err := h.inventoryService.GetInventoryReportSummary(ctx, outletID, contextInfo.OrganizationID, dateFrom, dateTo)
	if err != nil {
		logger.FromContext(ctx).WithError(err).Error("InventoryHandler::GetInventoryReportSummary -> Failed to get inventory report summary from service")
		responseError := contract.NewResponseError(constants.InternalServerErrorCode, constants.RequestEntity, err.Error())
		util.HandleResponse(c.Writer, c.Request, contract.BuildErrorResponse([]*contract.ResponseError{responseError}), "InventoryHandler::GetInventoryReportSummary")
		return
	}

	response := contract.BuildSuccessResponse(summary)
	util.HandleResponse(c.Writer, c.Request, response, "InventoryHandler::GetInventoryReportSummary")
}

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

	filter := &models.InventoryReportFilter{}

	if outletIDStr := c.Param("outlet_id"); outletIDStr != "" {
		outletID, err := uuid.Parse(outletIDStr)
		if err != nil {
			logger.FromContext(ctx).WithError(err).Error("InventoryHandler::GetInventoryReportDetails -> Invalid outlet ID")
			validationResponseError := contract.NewResponseError(constants.MalformedFieldErrorCode, constants.RequestEntity, "Invalid outlet ID")
			util.HandleResponse(c.Writer, c.Request, contract.BuildErrorResponse([]*contract.ResponseError{validationResponseError}), "InventoryHandler::GetInventoryReportDetails")
			return
		}
		filter.OutletID = &outletID
	} else {
		logger.FromContext(ctx).Error("InventoryHandler::GetInventoryReportDetails -> Missing outlet_id parameter")
		validationResponseError := contract.NewResponseError(constants.MissingFieldErrorCode, constants.RequestEntity, "outlet_id is required")
		util.HandleResponse(c.Writer, c.Request, contract.BuildErrorResponse([]*contract.ResponseError{validationResponseError}), "InventoryHandler::GetInventoryReportDetails")
		return
	}

	if categoryIDStr := c.Query("category_id"); categoryIDStr != "" {
		categoryID, err := uuid.Parse(categoryIDStr)
		if err != nil {
			logger.FromContext(ctx).WithError(err).Error("InventoryHandler::GetInventoryReportDetails -> Invalid category ID")
			validationResponseError := contract.NewResponseError(constants.MalformedFieldErrorCode, constants.RequestEntity, "Invalid category ID")
			util.HandleResponse(c.Writer, c.Request, contract.BuildErrorResponse([]*contract.ResponseError{validationResponseError}), "InventoryHandler::GetInventoryReportDetails")
			return
		}
		filter.CategoryID = &categoryID
	}

	// Parse show_low_stock (optional)
	if showLowStockStr := c.Query("show_low_stock"); showLowStockStr != "" {
		if showLowStock, err := strconv.ParseBool(showLowStockStr); err == nil {
			filter.ShowLowStock = &showLowStock
		}
	}

	// Parse show_zero_stock (optional)
	if showZeroStockStr := c.Query("show_zero_stock"); showZeroStockStr != "" {
		if showZeroStock, err := strconv.ParseBool(showZeroStockStr); err == nil {
			filter.ShowZeroStock = &showZeroStock
		}
	}

	// Parse search (optional)
	if search := c.Query("search"); search != "" {
		filter.Search = &search
	}

	// Parse limit (optional)
	if limitStr := c.Query("limit"); limitStr != "" {
		if limit, err := strconv.Atoi(limitStr); err == nil && limit > 0 {
			filter.Limit = &limit
		}
	}

	// Parse offset (optional)
	if offsetStr := c.Query("offset"); offsetStr != "" {
		if offset, err := strconv.Atoi(offsetStr); err == nil && offset >= 0 {
			filter.Offset = &offset
		}
	}

	dateFromStr := c.Query("date_from")
	dateToStr := c.Query("date_to")

	if fromTime, toTime, err := util.ParseDateRangeToJakartaTime(dateFromStr, dateToStr); err == nil {
		if fromTime != nil {
			filter.DateFrom = fromTime
		}
		if toTime != nil {
			filter.DateTo = toTime
		}
	}

	report, err := h.inventoryService.GetInventoryReportDetails(ctx, filter, contextInfo.OrganizationID)
	if err != nil {
		logger.FromContext(ctx).WithError(err).Error("InventoryHandler::GetInventoryReportDetails -> Failed to get inventory report details from service")
		responseError := contract.NewResponseError(constants.InternalServerErrorCode, constants.RequestEntity, err.Error())
		util.HandleResponse(c.Writer, c.Request, contract.BuildErrorResponse([]*contract.ResponseError{responseError}), "InventoryHandler::GetInventoryReportDetails")
		return
	}

	response := contract.BuildSuccessResponse(report)
	util.HandleResponse(c.Writer, c.Request, response, "InventoryHandler::GetInventoryReportDetails")
}
