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

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

type CustomerHandler struct {
	customerService   service.CustomerService
	customerValidator validator.CustomerValidator
}

func NewCustomerHandler(
	customerService service.CustomerService,
	customerValidator validator.CustomerValidator,
) *CustomerHandler {
	return &CustomerHandler{
		customerService:   customerService,
		customerValidator: customerValidator,
	}
}

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

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

	validationError, validationErrorCode := h.customerValidator.ValidateCreateCustomerRequest(&req)
	if validationError != nil {
		logger.FromContext(c.Request.Context()).WithError(validationError).Error("CustomerHandler::CreateCustomer -> request validation failed")
		validationResponseError := contract.NewResponseError(validationErrorCode, constants.RequestEntity, validationError.Error())
		util.HandleResponse(c.Writer, c.Request, contract.BuildErrorResponse([]*contract.ResponseError{validationResponseError}), "CustomerHandler::CreateCustomer")
		return
	}

	organizationID := contextInfo.OrganizationID
	customerResponse, err := h.customerService.CreateCustomer(c.Request.Context(), &req, organizationID)
	if err != nil {
		logger.FromContext(c.Request.Context()).WithError(err).Error("CustomerHandler::CreateCustomer -> Failed to create customer from service")
		errorResp := contract.NewResponseError(constants.InternalServerErrorCode, constants.CustomerServiceEntity, err.Error())
		util.HandleResponse(c.Writer, c.Request, contract.BuildErrorResponse([]*contract.ResponseError{errorResp}), "CustomerHandler::CreateCustomer")
		return
	}

	logger.FromContext(c.Request.Context()).Infof("CustomerHandler::CreateCustomer -> Successfully created customer = %+v", customerResponse)
	util.HandleResponse(c.Writer, c.Request, contract.BuildSuccessResponse(customerResponse), "CustomerHandler::CreateCustomer")
}

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

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

	organizationID := contextInfo.OrganizationID
	customerResponse, err := h.customerService.GetCustomer(c.Request.Context(), customerID, organizationID)
	if err != nil {
		logger.FromContext(ctx).WithError(err).Error("CustomerHandler::GetCustomer -> Failed to get customer from service")
		errorResp := contract.NewResponseError(constants.InternalServerErrorCode, constants.CustomerServiceEntity, err.Error())
		util.HandleResponse(c.Writer, c.Request, contract.BuildErrorResponse([]*contract.ResponseError{errorResp}), "CustomerHandler::GetCustomer")
		return
	}

	util.HandleResponse(c.Writer, c.Request, contract.BuildSuccessResponse(customerResponse), "CustomerHandler::GetCustomer")
}

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

	req := &contract.ListCustomersRequest{
		Page:  1,
		Limit: 10,
	}

	// Parse query parameters
	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
		}
	}

	req.Search = c.Query("search")
	req.SortBy = c.Query("sort_by")
	req.SortOrder = c.Query("sort_order")

	if isActiveStr := c.Query("is_active"); isActiveStr != "" {
		if isActive, err := strconv.ParseBool(isActiveStr); err == nil {
			req.IsActive = &isActive
		}
	}

	if isDefaultStr := c.Query("is_default"); isDefaultStr != "" {
		if isDefault, err := strconv.ParseBool(isDefaultStr); err == nil {
			req.IsDefault = &isDefault
		}
	}

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

	organizationID := contextInfo.OrganizationID
	customersResponse, err := h.customerService.ListCustomers(c.Request.Context(), req, organizationID)
	if err != nil {
		logger.FromContext(ctx).WithError(err).Error("CustomerHandler::ListCustomers -> Failed to list customers from service")
		errorResp := contract.NewResponseError(constants.InternalServerErrorCode, constants.CustomerServiceEntity, err.Error())
		util.HandleResponse(c.Writer, c.Request, contract.BuildErrorResponse([]*contract.ResponseError{errorResp}), "CustomerHandler::ListCustomers")
		return
	}

	util.HandleResponse(c.Writer, c.Request, contract.BuildSuccessResponse(customersResponse), "CustomerHandler::ListCustomers")
}

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

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

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

	validationError, validationErrorCode := h.customerValidator.ValidateUpdateCustomerRequest(&req)
	if validationError != nil {
		logger.FromContext(c.Request.Context()).WithError(validationError).Error("CustomerHandler::UpdateCustomer -> request validation failed")
		validationResponseError := contract.NewResponseError(validationErrorCode, constants.RequestEntity, validationError.Error())
		util.HandleResponse(c.Writer, c.Request, contract.BuildErrorResponse([]*contract.ResponseError{validationResponseError}), "CustomerHandler::UpdateCustomer")
		return
	}

	organizationID := contextInfo.OrganizationID
	customerResponse, err := h.customerService.UpdateCustomer(c.Request.Context(), customerID, organizationID, &req)
	if err != nil {
		logger.FromContext(ctx).WithError(err).Error("CustomerHandler::UpdateCustomer -> Failed to update customer from service")
		errorResp := contract.NewResponseError(constants.InternalServerErrorCode, constants.CustomerServiceEntity, err.Error())
		util.HandleResponse(c.Writer, c.Request, contract.BuildErrorResponse([]*contract.ResponseError{errorResp}), "CustomerHandler::UpdateCustomer")
		return
	}

	util.HandleResponse(c.Writer, c.Request, contract.BuildSuccessResponse(customerResponse), "CustomerHandler::UpdateCustomer")
}

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

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

	organizationID := contextInfo.OrganizationID
	err = h.customerService.DeleteCustomer(c.Request.Context(), customerID, organizationID)
	if err != nil {
		logger.FromContext(ctx).WithError(err).Error("CustomerHandler::DeleteCustomer -> Failed to delete customer from service")
		errorResp := contract.NewResponseError(constants.InternalServerErrorCode, constants.CustomerServiceEntity, err.Error())
		util.HandleResponse(c.Writer, c.Request, contract.BuildErrorResponse([]*contract.ResponseError{errorResp}), "CustomerHandler::DeleteCustomer")
		return
	}

	util.HandleResponse(c.Writer, c.Request, contract.BuildSuccessResponse(gin.H{"message": "Customer deleted successfully"}), "CustomerHandler::DeleteCustomer")
}

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

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

	validationError, validationErrorCode := h.customerValidator.ValidateSetDefaultCustomerRequest(&req)
	if validationError != nil {
		logger.FromContext(c.Request.Context()).WithError(validationError).Error("CustomerHandler::SetDefaultCustomer -> request validation failed")
		validationResponseError := contract.NewResponseError(validationErrorCode, constants.RequestEntity, validationError.Error())
		util.HandleResponse(c.Writer, c.Request, contract.BuildErrorResponse([]*contract.ResponseError{validationResponseError}), "CustomerHandler::SetDefaultCustomer")
		return
	}

	organizationID := contextInfo.OrganizationID
	customerResponse, err := h.customerService.SetDefaultCustomer(c.Request.Context(), req.CustomerID, organizationID)
	if err != nil {
		logger.FromContext(ctx).WithError(err).Error("CustomerHandler::SetDefaultCustomer -> Failed to set default customer from service")
		errorResp := contract.NewResponseError(constants.InternalServerErrorCode, constants.CustomerServiceEntity, err.Error())
		util.HandleResponse(c.Writer, c.Request, contract.BuildErrorResponse([]*contract.ResponseError{errorResp}), "CustomerHandler::SetDefaultCustomer")
		return
	}

	util.HandleResponse(c.Writer, c.Request, contract.BuildSuccessResponse(customerResponse), "CustomerHandler::SetDefaultCustomer")
}

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

	organizationID := contextInfo.OrganizationID
	customerResponse, err := h.customerService.GetDefaultCustomer(c.Request.Context(), organizationID)
	if err != nil {
		logger.FromContext(ctx).WithError(err).Error("CustomerHandler::GetDefaultCustomer -> Failed to get default customer from service")
		errorResp := contract.NewResponseError(constants.InternalServerErrorCode, constants.CustomerServiceEntity, err.Error())
		util.HandleResponse(c.Writer, c.Request, contract.BuildErrorResponse([]*contract.ResponseError{errorResp}), "CustomerHandler::GetDefaultCustomer")
		return
	}

	util.HandleResponse(c.Writer, c.Request, contract.BuildSuccessResponse(customerResponse), "CustomerHandler::GetDefaultCustomer")
}
