package handlers

import (
	"Yimaru-Backend/internal/domain"
	"Yimaru-Backend/internal/services/authentication"
	jwtutil "Yimaru-Backend/internal/web_server/jwt"
	"Yimaru-Backend/internal/web_server/response"
	"context"
	"encoding/json"
	"errors"
	"fmt"
	"io"
	"net/http"
	"os"
	"path/filepath"
	"strconv"
	"time"

	"github.com/gofiber/fiber/v2"
	"github.com/google/uuid"
	"go.uber.org/zap"
)

// CheckProfileCompleted godoc
// @Summary Check if user profile is completed
// @Description Returns the profile completion status and percentage for the specified user
// @Tags user
// @Accept json
// @Produce json
// @Param user_id path int true "User ID"
// @Success 200 {object} domain.Response
// @Failure 400 {object} domain.ErrorResponse
// @Failure 404 {object} domain.ErrorResponse
// @Failure 500 {object} domain.ErrorResponse
// @Router /api/v1/user/{user_id}/is-profile-completed [get]
func (h *Handler) CheckProfileCompleted(c *fiber.Ctx) error {
	userIDParam := c.Params("user_id")
	if userIDParam == "" {
		return c.Status(fiber.StatusBadRequest).JSON(domain.ErrorResponse{
			Message: "Invalid user id",
			Error:   "User id cannot be empty",
		})
	}

	userID, err := strconv.ParseInt(userIDParam, 10, 64)
	if err != nil || userID <= 0 {
		return c.Status(fiber.StatusBadRequest).JSON(domain.ErrorResponse{
			Message: "Invalid user id",
			Error:   "User id must be a valid positive integer",
		})
	}

	status, err := h.userSvc.GetProfileCompletionStatus(c.Context(), userID)
	if err != nil {
		if errors.Is(err, authentication.ErrUserNotFound) {
			return c.Status(fiber.StatusNotFound).JSON(domain.ErrorResponse{
				Message: "User not found",
				Error:   err.Error(),
			})
		}

		return c.Status(fiber.StatusInternalServerError).JSON(domain.ErrorResponse{
			Message: "Failed to check profile completion status",
			Error:   err.Error(),
		})
	}

	return c.Status(fiber.StatusOK).JSON(domain.Response{
		Message: "Profile completion status fetched successfully",
		Data: map[string]interface{}{
			"is_profile_completed":            status.IsCompleted,
			"profile_completion_percentage":   status.Percentage,
		},
	})
}

// UpdateUser godoc
// @Summary Update user profile
// @Description Updates user profile information (partial updates supported)
// @Tags user
// @Accept json
// @Produce json
// @Param user_id path int true "User ID"
// @Param body body domain.UpdateUserReq true "Update user payload"
// @Success 200 {object} domain.Response
// @Failure 400 {object} domain.ErrorResponse
// @Failure 404 {object} domain.ErrorResponse
// @Failure 500 {object} domain.ErrorResponse
// @Router /api/v1/user [put]
func (h *Handler) UpdateUser(c *fiber.Ctx) error {
	// Extract user ID from context
	userID, ok := c.Locals("user_id").(int64)
	if !ok {
		return c.Status(fiber.StatusBadRequest).JSON(domain.ErrorResponse{
			Message: "Invalid user context",
			Error:   "User ID not found in request context",
		})
	}

	// userID, err := strconv.ParseInt(userIDStr, 10, 64)
	// if err != nil || userID <= 0 {
	// 	return c.Status(fiber.StatusBadRequest).JSON(domain.ErrorResponse{
	// 		Message: "Invalid user ID",
	// 		Error:   "User ID must be a positive integer",
	// 	})
	// }

	// Parse request body
	var req domain.UpdateUserReq
	if err := c.BodyParser(&req); err != nil {
		return c.Status(fiber.StatusBadRequest).JSON(domain.ErrorResponse{
			Message: "Invalid request body",
			Error:   err.Error(),
		})
	}

	// Enforce user identity
	req.UserID = userID

	// Optional: lightweight validation (example)
	// if req.Status.IsSet() {
	// 	if !domain.(req.Status.Value) {
	// 		return c.Status(fiber.StatusBadRequest).JSON(domain.ErrorResponse{
	// 			Message: "Invalid status value",
	// 			Error:   "Unsupported user status",
	// 		})
	// 	}
	// }

	// Call service
	if err := h.userSvc.UpdateUser(c.Context(), req); err != nil {
		if errors.Is(err, authentication.ErrUserNotFound) {
			return c.Status(fiber.StatusNotFound).JSON(domain.ErrorResponse{
				Message: "User not found",
				Error:   err.Error(),
			})
		}

		return c.Status(fiber.StatusInternalServerError).JSON(domain.ErrorResponse{
			Message: "Failed to update user",
			Error:   err.Error(),
		})
	}

	actorRole := string(c.Locals("role").(domain.Role))
	ip := c.IP()
	ua := c.Get("User-Agent")
	meta, _ := json.Marshal(map[string]interface{}{"user_id": userID})
	go h.activityLogSvc.RecordAction(context.Background(), &userID, &actorRole, domain.ActionUserUpdated, domain.ResourceUser, &userID, fmt.Sprintf("Updated user ID: %d", userID), meta, &ip, &ua)

	return c.Status(fiber.StatusOK).JSON(domain.Response{
		Message: "User updated successfully",
	})
}

// UpdateUserKnowledgeLevel godoc
// @Summary Update user's knowledge level
// @Description Updates the knowledge level of the specified user after initial assessment
// @Tags user
// @Accept json
// @Produce json
// @Param user_id path int true "User ID"
// @Param knowledge_level body domain.UpdateKnowledgeLevelReq true "Knowledge level"
// @Success 200 {object} domain.Response
// @Failure 400 {object} domain.ErrorResponse
// @Failure 404 {object} domain.ErrorResponse
// @Failure 500 {object} domain.ErrorResponse
// @Router /api/v1/{tenant_slug}/user/knowledge-level [put]
func (h *Handler) UpdateUserKnowledgeLevel(c *fiber.Ctx) error {
	userID, ok := c.Locals("user_id").(int64)
	if !ok {
		return c.Status(fiber.StatusBadRequest).JSON(domain.ErrorResponse{
			Message: "Invalid user context",
			Error:   "User ID not found in request context",
		})
	}

	var req domain.UpdateKnowledgeLevelReq
	if err := c.BodyParser(&req); err != nil || req.KnowledgeLevel == "" {
		return c.Status(fiber.StatusBadRequest).JSON(domain.ErrorResponse{
			Message: "Invalid request body",
			Error:   "Knowledge level is required",
		})
	}

	err := h.userSvc.UpdateUserKnowledgeLevel(c.Context(), userID, req.KnowledgeLevel)
	if err != nil {
		if errors.Is(err, authentication.ErrUserNotFound) {
			return c.Status(fiber.StatusNotFound).JSON(domain.ErrorResponse{
				Message: "User not found",
				Error:   err.Error(),
			})
		}
		return c.Status(fiber.StatusInternalServerError).JSON(domain.ErrorResponse{
			Message: "Failed to update user knowledge level",
			Error:   err.Error(),
		})
	}

	h.sendInAppNotification(userID, domain.NOTIFICATION_TYPE_KNOWLEDGE_LEVEL_UPDATE, "Knowledge Level Updated", "Your knowledge level has been updated to: "+req.KnowledgeLevel)

	return c.Status(fiber.StatusOK).JSON(domain.Response{
		Message: "User knowledge level updated successfully",
	})
}

// ResendOtp godoc
// @Summary Resend OTP
// @Description Resend OTP if the previous one is expired
// @Tags otp
// @Accept json
// @Produce json
// @Param resendOtp body domain.ResendOtpReq true "Resend OTP"
// @Success 200 {object} response.APIResponse
// @Failure 400 {object} response.APIResponse
// @Failure 500 {object} response.APIResponse
// @Router /api/v1/{tenant_slug}/otp/resend [post]
func (h *Handler) ResendOtp(c *fiber.Ctx) error {
	var req domain.ResendOtpReq
	if err := c.BodyParser(&req); err != nil {
		h.mongoLoggerSvc.Info("Failed to parse ResendOtp request",
			zap.Int("status_code", fiber.StatusBadRequest),
			zap.Error(err),
			zap.Time("timestamp", time.Now()),
		)

		return c.Status(fiber.StatusBadRequest).JSON(domain.ErrorResponse{
			Message: "Failed to resend OTP",
			Error:   "Invalid request body: " + err.Error(),
		})
	}

	if valErrs, ok := h.validator.Validate(c, req); !ok {
		var errMsg string
		for field, msg := range valErrs {
			errMsg += fmt.Sprintf("%s: %s; ", field, msg)
		}

		return c.Status(fiber.StatusBadRequest).JSON(domain.ErrorResponse{
			Message: "Failed to resend OTP",
			Error:   errMsg,
		})
	}

	user, err := h.userSvc.GetUserByEmailPhone(c.Context(), req.Email, req.PhoneNumber)
	if err != nil {
		h.mongoLoggerSvc.Info("Failed to get user by user name",
			zap.String("email", req.Email),
			zap.String("phone_number", req.PhoneNumber),
			zap.Int("status_code", fiber.StatusBadRequest),
			zap.Error(err),
			zap.Time("timestamp", time.Now()),
		)

		return c.Status(fiber.StatusBadRequest).JSON(domain.ErrorResponse{
			Message: "Failed to resend OTP",
			Error:   err.Error(),
		})
	}

	medium, err := getMedium(user.Email, user.PhoneNumber)
	if err != nil {
		h.mongoLoggerSvc.Info("Failed to determine OTP medium",
			zap.String("email", user.Email),
			zap.String("phone_number", user.PhoneNumber),
			zap.Int("status_code", fiber.StatusBadRequest),
			zap.Error(err),
			zap.Time("timestamp", time.Now()),
		)

		return c.Status(fiber.StatusBadRequest).JSON(domain.ErrorResponse{
			Message: "Failed to resend OTP",
			Error:   err.Error(),
		})
	}

	sentTo := user.Email
	if medium == domain.OtpMediumSms {
		sentTo = user.PhoneNumber
	}

	if err := h.userSvc.ResendOtp(
		c.Context(),
		req.Email,
		req.PhoneNumber,
	); err != nil {

		h.mongoLoggerSvc.Error("Failed to resend OTP",
			zap.String("sent_to", sentTo),
			zap.String("medium", string(medium)),
			zap.Int("status_code", fiber.StatusBadRequest),
			zap.Error(err),
			zap.Time("timestamp", time.Now()),
		)

		return c.Status(fiber.StatusBadRequest).JSON(domain.ErrorResponse{
			Message: "Failed to resend OTP",
			Error:   err.Error(),
		})
	}

	return c.Status(fiber.StatusOK).JSON(domain.Response{
		Message:    "OTP resent successfully",
		Success:    true,
		StatusCode: fiber.StatusOK,
		Data:       nil,
	})
}

// CheckUserNameUnique godoc
// @Summary Check if user_name is unique
// @Description Returns whether the specified user_name is available (unique)
// @Tags user
// @Accept json
// @Produce json
// @Param user_name path string true "User Name"
// @Success 200 {object} domain.Response
// @Failure 400 {object} domain.ErrorResponse
// @Failure 500 {object} domain.ErrorResponse
// @Router /api/v1/user/{user_name}/is-unique [get]
// func (h *Handler) CheckUserNameUnique(c *fiber.Ctx) error {
// 	userName := c.Params("user_name")
// 	if userName == "" {
// 		return c.Status(fiber.StatusBadRequest).JSON(domain.ErrorResponse{
// 			Message: "Invalid user name",
// 			Error:   "user_name path parameter cannot be empty",
// 		})
// 	}

// 	isUnique, err := h.userSvc.IsUserNameUnique(c.Context(), userName)
// 	if err != nil {
// 		return c.Status(fiber.StatusInternalServerError).JSON(domain.ErrorResponse{
// 			Message: "Failed to check user name uniqueness",
// 			Error:   err.Error(),
// 		})
// 	}

// 	return c.Status(fiber.StatusOK).JSON(domain.Response{
// 		Message: "User name uniqueness checked successfully",
// 		Data: map[string]bool{
// 			"is_unique": isUnique,
// 		},
// 	})
// }

// CheckUserPending godoc
// @Summary Check if user status is pending
// @Description Returns whether the specified user has a status of "pending"
// @Tags user
// @Accept json
// @Produce json
// @Param user_id path string true "User ID"
// @Success 200 {object} domain.Response
// @Failure 400 {object} domain.ErrorResponse
// @Failure 404 {object} domain.ErrorResponse
// @Failure 500 {object} domain.ErrorResponse
// @Router /api/v1/user/{user_id}/is-pending [get]
func (h *Handler) CheckUserPending(c *fiber.Ctx) error {
	userID, err := strconv.ParseInt(c.Params("user_id"), 10, 64)
	if err != nil {
		return c.Status(fiber.StatusBadRequest).JSON(domain.ErrorResponse{
			Message: "Invalid user ID",
			Error:   err.Error(),
		})
	}
	if userID == 0 {
		return c.Status(fiber.StatusBadRequest).JSON(domain.ErrorResponse{
			Message: "Invalid user ID",
			Error:   "User ID cannot be empty",
		})
	}

	isPending, err := h.userSvc.IsUserPending(c.Context(), userID)
	if err != nil {
		if errors.Is(err, authentication.ErrUserNotFound) {
			return c.Status(fiber.StatusNotFound).JSON(domain.ErrorResponse{
				Message: "User not found",
				Error:   err.Error(),
			})
		}
		return c.Status(fiber.StatusInternalServerError).JSON(domain.ErrorResponse{
			Message: "Failed to check user status",
			Error:   err.Error(),
		})
	}

	return c.Status(fiber.StatusOK).JSON(domain.Response{
		Message: "User status fetched successfully",
		Data: map[string]bool{
			"is_pending": isPending,
		},
	})
}

// GetAllUsers godoc
// @Summary Get all users
// @Description Get users with optional filters
// @Tags user
// @Accept json
// @Produce json
// @Param role query string false "Role filter"
// @Param query query string false "Search query"
// @Param page query int false "Page number"
// @Param page_size query int false "Page size"
// @Param created_before query string false "Created before (RFC3339)"
// @Param created_after query string false "Created after (RFC3339)"
// @Success 200 {object} response.APIResponse
// @Failure 400 {object} response.APIResponse
// @Failure 500 {object} response.APIResponse
// @Router /api/v1/users [get]
func (h *Handler) GetAllUsers(c *fiber.Ctx) error {
	searchQuery := c.Query("query")
	searchString := domain.ValidString{
		Value: searchQuery,
		// Valid: searchQuery != "",
	}

	createdBeforeQuery := c.Query("created_before")
	var createdBefore domain.ValidTime
	if createdBeforeQuery != "" {
		parsed, err := time.Parse(time.RFC3339, createdBeforeQuery)
		if err != nil {
			h.logger.Info("invalid created_before format", "error", err)
			return fiber.NewError(fiber.StatusBadRequest, "Invalid created_before format")
		}
		createdBefore = domain.ValidTime{Value: parsed, Valid: true}
	}

	createdAfterQuery := c.Query("created_after")
	var createdAfter domain.ValidTime
	if createdAfterQuery != "" {
		parsed, err := time.Parse(time.RFC3339, createdAfterQuery)
		if err != nil {
			h.logger.Info("invalid created_after format", "error", err)
			return fiber.NewError(fiber.StatusBadRequest, "Invalid created_after format")
		}
		createdAfter = domain.ValidTime{Value: parsed, Valid: true}
	}

	filter := domain.UserFilter{
		Role:          c.Query("role"),
		Page:          int64(c.QueryInt("page", 1) - 1),
		PageSize:      int64(c.QueryInt("page_size", 10)),
		Query:         searchString.Value,
		CreatedBefore: createdBefore,
		CreatedAfter:  createdAfter,
	}

	if valErrs, ok := h.validator.Validate(c, filter); !ok {
		var errMsg string
		for f, msg := range valErrs {
			errMsg += fmt.Sprintf("%s: %s; ", f, msg)
		}
		h.mongoLoggerSvc.Info("invalid filter values in GetAllUsers request",
			zap.Int("status_code", fiber.StatusBadRequest),
			zap.Any("validation_errors", valErrs),
			zap.Time("timestamp", time.Now()))
		return fiber.NewError(fiber.StatusBadRequest, errMsg)
	}

	users, total, err := h.userSvc.GetAllUsers(c.Context(), filter)
	if err != nil {
		h.mongoLoggerSvc.Error("failed to get users",
			zap.Int("status_code", fiber.StatusInternalServerError),
			zap.Any("filter", filter),
			zap.Error(err),
			zap.Time("timestamp", time.Now()))
		return fiber.NewError(fiber.StatusInternalServerError, "Failed to get users: "+err.Error())
	}

	// Map to profile response to avoid leaking sensitive fields
	// result := make([]domain.UserProfileResponse, len(users))
	// for i, u := range users {
	// 	result[i] = domain{
	// 		ID:                u.ID,
	// 		FirstName:         u.FirstName,
	// 		LastName:          u.LastName,
	// 		Gender:            u.Gender,
	// 		Email:             u.Email,
	// 		PhoneNumber:       u.PhoneNumber,
	// 		Role:              u.Role,
	// 		Age:               u.Age,
	// 		EducationLevel:    u.EducationLevel,
	// 		Country:           u.Country,
	// 		Region:            u.Region,
	// 		NickName:          u.NickName,
	// 		Occupation:        u.Occupation,
	// 		LearningGoal:      u.LearningGoal,
	// 		LanguageGoal:      u.LanguageGoal,
	// 		LanguageChallange: u.LanguageChallange,
	// 		FavouriteTopic:    u.FavouriteTopic,
	// 		EmailVerified:     u.EmailVerified,
	// 		PhoneVerified:     u.PhoneVerified,
	// 		LastLogin:         u.LastLogin,
	// 		ProfileCompleted:  u.ProfileCompleted,
	// 		ProfilePictureURL: u.ProfilePictureURL,
	// 		PreferredLanguage: u.PreferredLanguage,
	// 		CreatedAt:         u.CreatedAt,
	// 		UpdatedAt:         u.UpdatedAt,
	// 	}
	// }

	// Map domain.User -> domain.UserProfileResponse to control fields and formatting
	mapped := make([]domain.UserProfileResponse, 0, len(users))
	for _, u := range users {
		var bd string
		if !u.BirthDay.IsZero() {
			bd = u.BirthDay.Format("2006-01-02")
		}
		mapped = append(mapped, domain.UserProfileResponse{
			ID:                         u.ID,
			FirstName:                  u.FirstName,
			LastName:                   u.LastName,
			Gender:                     u.Gender,
			BirthDay:                   bd,
			Email:                      u.Email,
			PhoneNumber:                u.PhoneNumber,
			Role:                       u.Role,
			AgeGroup:                   u.AgeGroup,
			EducationLevel:             u.EducationLevel,
			Country:                    u.Country,
			Region:                     u.Region,
			InitialAssessmentCompleted: u.InitialAssessmentCompleted,
			NickName:                   u.NickName,
			Occupation:                 u.Occupation,
			LearningGoal:               u.LearningGoal,
			LanguageGoal:               u.LanguageGoal,
			LanguageChallange:          u.LanguageChallange,
			FavouriteTopic:             u.FavouriteTopic,
			EmailVerified:              u.EmailVerified,
			PhoneVerified:              u.PhoneVerified,
			Status:                     u.Status,
			LastLogin:                  u.LastLogin,
			ProfileCompleted:           u.ProfileCompleted,
			ProfilePictureURL:          u.ProfilePictureURL,
			PreferredLanguage:          u.PreferredLanguage,
			CreatedAt:                  u.CreatedAt,
			UpdatedAt:                  u.UpdatedAt,
		})
	}

	return response.WriteJSON(c, fiber.StatusOK, "Users fetched successfully", map[string]interface{}{"users": mapped, "total": total}, nil)
}

// VerifyOtp godoc
// @Summary Verify OTP
// @Description Verify OTP for registration or other actions
// @Tags user
// @Accept json
// @Produce json
// @Param verifyOtp body domain.VerifyOtpReq true "Verify OTP"
// @Success 200 {object} response.APIResponse
// @Failure 400 {object} response.APIResponse
// @Failure 500 {object} response.APIResponse
// @Router /api/v1/user/verify-otp [post]
func (h *Handler) VerifyOtp(c *fiber.Ctx) error {
	var req domain.VerifyOtpReq
	if err := c.BodyParser(&req); err != nil {
		h.mongoLoggerSvc.Info("Failed to parse VerifyOtp request",
			zap.Int("status_code", fiber.StatusBadRequest),
			zap.Error(err),
			zap.Time("timestamp", time.Now()),
		)
		return c.Status(fiber.StatusBadRequest).JSON(domain.ErrorResponse{
			Message: "Failed to verify OTP",
			Error:   "Invalid request body: " + err.Error(),
		})
	}

	if valErrs, ok := h.validator.Validate(c, req); !ok {
		var errMsg string
		for field, msg := range valErrs {
			errMsg += fmt.Sprintf("%s: %s; ", field, msg)
		}
		return c.Status(fiber.StatusBadRequest).JSON(domain.ErrorResponse{
			Message: "Failed to verify OTP",
			Error:   errMsg,
		})
	}

	// Call service to verify OTP
	loginSuccess, err := h.authSvc.VerifyOtp(c.Context(), req.Email, req.PhoneNumber, req.Otp)
	if err != nil {
		var errMsg string
		switch {
		case errors.Is(err, domain.ErrOtpAlreadyUsed):
			errMsg = "OTP already used"
		case errors.Is(err, domain.ErrOtpExpired):
			errMsg = "OTP expired"
		case errors.Is(err, domain.ErrInvalidOtp):
			errMsg = "Invalid OTP"
		case errors.Is(err, domain.ErrOtpNotFound):
			errMsg = "OTP not found"
		default:
			h.mongoLoggerSvc.Error("Failed to verify OTP",
				zap.Int("status_code", fiber.StatusInternalServerError),
				zap.Error(err),
				zap.Time("timestamp", time.Now()),
			)
			errMsg = "Failed to verify OTP: " + err.Error()
		}
		statusCode := fiber.StatusBadRequest
		if errMsg == "Failed to verify OTP: "+err.Error() {
			statusCode = fiber.StatusInternalServerError
		}
		return c.Status(statusCode).JSON(domain.ErrorResponse{
			Message: "Failed to verify OTP",
			Error:   errMsg,
		})
	}

	accessToken, err := jwtutil.CreateJwt(
		loginSuccess.UserId,
		loginSuccess.Role,
		h.jwtConfig.JwtAccessKey,
		h.jwtConfig.JwtAccessExpiry,
	)

	if err != nil {
		h.mongoLoggerSvc.Error("Failed to create access token",
			zap.Int("status_code", fiber.StatusInternalServerError),
			zap.Int64("user_id", loginSuccess.UserId),
			zap.Error(err),
			zap.Time("timestamp", time.Now()),
		)
		return c.Status(fiber.StatusInternalServerError).JSON(domain.ErrorResponse{
			Message: "Failed to verify otp",
			Error:   "Failed to generate access token",
		})
	}

	return c.Status(fiber.StatusOK).JSON(domain.Response{
		Message: "OTP verified successfully",
		Data: loginUserRes{
			AccessToken:  accessToken,
			RefreshToken: loginSuccess.RfToken,
			Role:         string(loginSuccess.Role),
			UserID:       loginSuccess.UserId,
		},
	})
}

type GetTenantSlugByToken struct {
	Slug string `json:"slug"`
}

// GetTenantSlugByToken godoc
// @Summary Check if phone number or email exist
// @Description Check if phone number or email exist
// @Tags user
// @Accept json
// @Produce json
// @Param checkPhoneEmailExist body CheckPhoneEmailExistReq true "Check phone number or email exist"
// @Success 200 {object} CheckPhoneEmailExistRes
// @Failure 400 {object} response.APIResponse
// @Failure 500 {object} response.APIResponse
// @Router /api/v1/tenant [get]
func (h *Handler) GetTenantSlugByToken(c *fiber.Ctx) error {
	userID, ok := c.Locals("user_id").(int64)
	if !ok || userID == 0 {
		h.mongoLoggerSvc.Error("Invalid user ID in context",
			zap.Int64("userID", userID),
			zap.Int("status_code", fiber.StatusInternalServerError),
			zap.Time("timestamp", time.Now()),
		)
		return fiber.NewError(fiber.StatusInternalServerError, "Invalid user identification")
	}

	_, err := h.userSvc.GetUserByID(c.Context(), userID)
	if err != nil {
		h.mongoLoggerSvc.Error("Failed to get user profile",
			zap.Int64("userID", userID),
			zap.Int("status_code", fiber.StatusInternalServerError),
			zap.Error(err),
			zap.Time("timestamp", time.Now()),
		)
		return fiber.NewError(fiber.StatusInternalServerError, "Failed to retrieve user profile:"+err.Error())
	}

	// if !user.OrganizationID.Valid {
	// 	if user.Role == domain.RoleSuperAdmin {
	// 		return fiber.NewError(fiber.StatusBadRequest, "Role Super-Admin Doesn't have a company-id")
	// 	}
	// 	h.mongoLoggerSvc.Error("Unknown Error: User doesn't have a company-id",
	// 		zap.Int64("userID", userID),
	// 		zap.Int("status_code", fiber.StatusInternalServerError),
	// 		zap.Error(err),
	// 		zap.Time("timestamp", time.Now()),
	// 	)
	// 	return fiber.NewError(fiber.StatusInternalServerError, "Unknown Error: User doesn't have a company-id")
	// }
	// company, err := h.companySvc.GetCompanyByID(c.Context(), user.CompanyID.Value)

	// if err != nil {
	// 	h.mongoLoggerSvc.Error("Failed to get company by id",
	// 		zap.Int64("company", user.CompanyID.Value),
	// 		zap.Int("status_code", fiber.StatusInternalServerError),
	// 		zap.Error(err),
	// 		zap.Time("timestamp", time.Now()),
	// 	)
	// 	return fiber.NewError(fiber.StatusInternalServerError, "Failed to retrieve company:"+err.Error())
	// }

	res := GetTenantSlugByToken{
		Slug: strconv.FormatInt(userID, 10),
	}

	return response.WriteJSON(c, fiber.StatusOK, "Tenant Slug retrieved successfully", res, nil)
}

type CheckPhoneEmailExistReq struct {
	Email       string `json:"email" example:"john.doe@example.com"`
	PhoneNumber string `json:"phone_number" example:"1234567890"`
}
type CheckPhoneEmailExistRes struct {
	EmailExist       bool `json:"email_exist"`
	PhoneNumberExist bool `json:"phone_number_exist"`
}

// CheckPhoneEmailExist godoc
// @Summary Check if phone number or email exist
// @Description Check if phone number or email exist
// @Tags user
// @Accept json
// @Produce json
// @Param checkPhoneEmailExist body CheckPhoneEmailExistReq true "Check phone number or email exist"
// @Success 200 {object} CheckPhoneEmailExistRes
// @Failure 400 {object} response.APIResponse
// @Failure 500 {object} response.APIResponse
// @Router /api/v1/{tenant_slug}/user/checkPhoneEmailExist [post]
func (h *Handler) CheckPhoneEmailExist(c *fiber.Ctx) error {
	// companyID := c.Locals("company_id").(domain.ValidInt64)
	// if !companyID.Valid {
	// 	h.BadRequestLogger().Error("invalid company id")
	// 	return fiber.NewError(fiber.StatusBadRequest, "invalid company id")
	// }
	var req CheckPhoneEmailExistReq
	if err := c.BodyParser(&req); err != nil {
		h.mongoLoggerSvc.Info("Failed to parse CheckPhoneEmailExist request",
			zap.Int("status_code", fiber.StatusInternalServerError),
			zap.Error(err),
			zap.Time("timestamp", time.Now()),
		)
		return fiber.NewError(fiber.StatusBadRequest, "Invalid request body:"+err.Error())
	}

	if valErrs, ok := h.validator.Validate(c, req); !ok {
		var errMsg string
		for field, msg := range valErrs {
			errMsg += fmt.Sprintf("%s: %s; ", field, msg)
		}
		return fiber.NewError(fiber.StatusBadRequest, errMsg)
	}

	emailExist, phoneExist, err := h.userSvc.CheckPhoneEmailExist(c.Context(), req.PhoneNumber, req.Email)
	if err != nil {
		h.mongoLoggerSvc.Error("Failed to check phone/email existence",
			zap.Any("request", req),
			zap.Int("status_code", fiber.StatusInternalServerError),
			zap.Error(err),
			zap.Time("timestamp", time.Now()),
		)
		return fiber.NewError(fiber.StatusInternalServerError, "Failed to check phone/email existence:"+err.Error())
	}

	res := CheckPhoneEmailExistRes{
		EmailExist:       emailExist,
		PhoneNumberExist: phoneExist,
	}
	return response.WriteJSON(c, fiber.StatusOK, "Check successful", res, nil)
}

type RegisterCodeReq struct {
	Email       string `json:"email" example:"john.doe@example.com"`
	PhoneNumber string `json:"phone_number" example:"1234567890"`
}

// SendRegisterCode godoc
// @Summary Send register code
// @Description Send register code
// @Tags user
// @Accept json
// @Produce json
// @Param registerCode body RegisterCodeReq true "Send register code"
// @Success 200 {object} response.APIResponse
// @Failure 400 {object} response.APIResponse
// @Failure 500 {object} response.APIResponse
// @Router /api/v1/{tenant_slug}/user/sendRegisterCode [post]
func (h *Handler) SendRegisterCode(c *fiber.Ctx) error {
	// companyID := c.Locals("company_id").(domain.ValidInt64)
	// if !companyID.Valid {
	// 	h.BadRequestLogger().Error("invalid company id")
	// 	return fiber.NewError(fiber.StatusBadRequest, "invalid company id")
	// }
	var req RegisterCodeReq
	if err := c.BodyParser(&req); err != nil {
		h.mongoLoggerSvc.Info("Failed to parse SendRegisterCode request",
			zap.Int("status_code", fiber.StatusBadRequest),
			zap.Error(err),
			zap.Time("timestamp", time.Now()),
		)
		return fiber.NewError(fiber.StatusBadRequest, "Invalid request body:"+err.Error())
	}

	if valErrs, ok := h.validator.Validate(c, req); !ok {
		var errMsg string
		for field, msg := range valErrs {
			errMsg += fmt.Sprintf("%s: %s; ", field, msg)
		}
		return fiber.NewError(fiber.StatusBadRequest, errMsg)
	}

	var sentTo string
	var medium domain.OtpMedium
	if req.Email != "" {
		sentTo = req.Email
		medium = domain.OtpMediumEmail
	} else if req.PhoneNumber != "" {
		sentTo = req.PhoneNumber
		medium = domain.OtpMediumSms
	} else {
		return fiber.NewError(fiber.StatusBadRequest, "Email or PhoneNumber must be provided")
	}

	if err := h.userSvc.SendRegisterCode(c.Context(), medium, sentTo, domain.AfroMessage); err != nil {
		h.mongoLoggerSvc.Error("Failed to send register code",
			zap.String("Medium", string(medium)),
			zap.String("Send To", string(sentTo)),
			zap.Int("status_code", fiber.StatusInternalServerError),
			zap.Error(err),
			zap.Time("timestamp", time.Now()),
		)
		return fiber.NewError(fiber.StatusInternalServerError, "Failed to send register code:"+err.Error())
	}

	return response.WriteJSON(c, fiber.StatusOK, "Code sent successfully", nil, nil)
}

// RegisterUser godoc
// @Summary Register user
// @Description Register user
// @Tags user
// @Accept json
// @Produce json
// @Param registerUser body domain.RegisterUserReq true "Register user"
// @Success 200 {object} response.APIResponse
// @Failure 400 {object} response.APIResponse
// @Failure 500 {object} response.APIResponse
// @Router /api/v1/{tenant_slug}/user/register [post]
func (h *Handler) RegisterUser(c *fiber.Ctx) error {
	var req domain.RegisterUserReq
	if err := c.BodyParser(&req); err != nil {
		h.mongoLoggerSvc.Info("Failed to parse RegisterUser request",
			zap.Int("status_code", fiber.StatusBadRequest),
			zap.Error(err),
			zap.Time("timestamp", time.Now()),
		)
		return c.Status(fiber.StatusBadRequest).JSON(domain.ErrorResponse{
			Message: "Failed to register user",
			Error:   "Invalid request body: " + err.Error(),
		})
	}

	if valErrs, ok := h.validator.Validate(c, req); !ok {
		var errMsg string
		for field, msg := range valErrs {
			errMsg += fmt.Sprintf("%s: %s; ", field, msg)
		}
		return c.Status(fiber.StatusBadRequest).JSON(domain.ErrorResponse{
			Message: "Failed to register user",
			Error:   errMsg,
		})
	}

	user := domain.RegisterUserReq{
		// FirstName:   req.FirstName,
		// LastName:    req.LastName,
		// UserName:    req.UserName,
		Email:       req.Email,
		PhoneNumber: req.PhoneNumber,
		Password:    req.Password,
		OtpMedium:   domain.OtpMediumEmail,
		// Role:              string(domain.RoleStudent),
		// Age:               req.Age,
		// EducationLevel:    req.EducationLevel,
		// Country:           req.Country,
		// Region:            req.Region,
		// PreferredLanguage: req.PreferredLanguage,

		// NickName:          req.NickName,
		// Occupation:        req.Occupation,
		// LearningGoal:      req.LearningGoal,
		// LanguageGoal:      req.LanguageGoal,
		// LanguageChallange: req.LanguageChallange,
		// FavouriteTopic:    req.FavouriteTopic,
	}

	medium, err := getMedium(req.Email, req.PhoneNumber)
	if err != nil {
		h.mongoLoggerSvc.Info("Failed to get medium",
			zap.String("email", req.Email),
			zap.String("phone number", req.PhoneNumber),
			zap.Int("status_code", fiber.StatusBadRequest),
			zap.Error(err),
			zap.Time("timestamp", time.Now()),
		)
		return c.Status(fiber.StatusBadRequest).JSON(domain.ErrorResponse{
			Message: "Failed to register user",
			Error:   err.Error(),
		})
	}
	user.OtpMedium = medium

	_, err = h.userSvc.RegisterUser(c.Context(), user)
	if err != nil {
		h.mongoLoggerSvc.Error("Failed to register user",
			zap.String("email", req.Email),
			zap.String("phone number", req.PhoneNumber),
			zap.Int("status_code", fiber.StatusInternalServerError),
			zap.Error(err),
			zap.Time("timestamp", time.Now()),
		)

		return c.Status(fiber.StatusBadRequest).JSON(domain.ErrorResponse{
			Message: "Failed to register user",
			Error:   err.Error(),
		})
	}

	return c.Status(fiber.StatusOK).JSON(domain.Response{
		Message: "Registration successful",
		Data:    nil,
	})
}

func MapRegisterReqToUser(req domain.RegisterUserReq) domain.User {
	return domain.User{
		// FirstName:   req.FirstName,
		// LastName:    req.LastName,
		// UserName:    req.UserName,
		Email:       req.Email,
		PhoneNumber: req.PhoneNumber,
		Password:    []byte(req.Password), // or hashed password
		Role:        domain.Role(req.Role),
		// Age:               req.Age,
		// EducationLevel:    req.EducationLevel,
		// Country:           req.Country,
		// Region:            req.Region,
		// PreferredLanguage: req.PreferredLanguage,

		// NickName:          req.NickName,
		// Occupation:        req.Occupation,
		// LearningGoal:      req.LearningGoal,
		// LanguageGoal:      req.LanguageGoal,
		// LanguageChallange: req.LanguageChallange,
		// FavouriteTopic:    req.FavouriteTopic,
	}
}

type ResetCodeReq struct {
	Email       string `json:"email" example:"john.doe@example.com"`
	PhoneNumber string `json:"phone_number" validate:"required_without=Email" example:"1234567890"`
	// Provider    domain.OtpProvider `json:"provider" validate:"required" example:"twilio"`
}

// SendResetCode godoc
// @Summary Send reset code
// @Description Send reset code
// @Tags user
// @Accept json
// @Produce json
// @Param resetCode body ResetCodeReq true "Send reset code"
// @Success 200 {object} response.APIResponse
// @Failure 400 {object} response.APIResponse
// @Failure 500 {object} response.APIResponse
// @Router /api/v1/user/sendResetCode [post]
func (h *Handler) SendResetCode(c *fiber.Ctx) error {
	var req ResetCodeReq
	if err := c.BodyParser(&req); err != nil {
		h.mongoLoggerSvc.Info("Failed to parse SendResetCode request",
			zap.Int("status_code", fiber.StatusBadRequest),
			zap.Error(err),
			zap.Time("timestamp", time.Now()),
		)
		return fiber.NewError(fiber.StatusBadRequest, "Invalid request body:"+err.Error())
	}

	if valErrs, ok := h.validator.Validate(c, req); !ok {
		var errMsg string
		for field, msg := range valErrs {
			errMsg += fmt.Sprintf("%s: %s; ", field, msg)
		}
		return fiber.NewError(fiber.StatusBadRequest, errMsg)
	}

	var sentTo string
	var medium domain.OtpMedium
	if req.Email != "" {
		sentTo = req.Email
		medium = domain.OtpMediumEmail
	} else if req.PhoneNumber != "" {
		sentTo = req.PhoneNumber
		medium = domain.OtpMediumSms
	} else {
		h.mongoLoggerSvc.Info("Email or PhoneNumber must be provided",
			zap.String("Email", req.Email),
			zap.String("Phone Number", req.PhoneNumber),
			zap.Int("status_code", fiber.StatusBadRequest),
			zap.Time("timestamp", time.Now()),
		)
		return fiber.NewError(fiber.StatusBadRequest, "Email or PhoneNumber must be provided")
	}

	if err := h.userSvc.SendResetCode(c.Context(), medium, sentTo, domain.AfroMessage); err != nil {
		h.mongoLoggerSvc.Error("Failed to send reset code",
			zap.String("medium", string(medium)),
			zap.String("sentTo", string(sentTo)),
			zap.Int("status_code", fiber.StatusInternalServerError),
			zap.Error(err),
			zap.Time("timestamp", time.Now()),
		)
		return fiber.NewError(fiber.StatusInternalServerError, "Failed to send reset code:"+err.Error())
	}

	return response.WriteJSON(c, fiber.StatusOK, "Code sent successfully", nil, nil)
}

// SendTenantResetCode godoc
// @Summary Send reset code
// @Description Send reset code
// @Tags user
// @Accept json
// @Produce json
// @Param resetCode body ResetCodeReq true "Send reset code"
// @Success 200 {object} response.APIResponse
// @Failure 400 {object} response.APIResponse
// @Failure 500 {object} response.APIResponse
// @Router /api/v1/{tenant_slug}/user/sendResetCode [post]
func (h *Handler) SendTenantResetCode(c *fiber.Ctx) error {
	// companyID := c.Locals("company_id").(domain.ValidInt64)
	// if !companyID.Valid {
	// 	h.BadRequestLogger().Error("invalid company id")
	// 	return fiber.NewError(fiber.StatusBadRequest, "invalid company id")
	// }
	var req ResetCodeReq
	if err := c.BodyParser(&req); err != nil {
		h.mongoLoggerSvc.Info("Failed to parse SendResetCode request",
			zap.Int("status_code", fiber.StatusBadRequest),
			zap.Error(err),
			zap.Time("timestamp", time.Now()),
		)
		return fiber.NewError(fiber.StatusBadRequest, "Invalid request body:"+err.Error())
	}

	if valErrs, ok := h.validator.Validate(c, req); !ok {
		var errMsg string
		for field, msg := range valErrs {
			errMsg += fmt.Sprintf("%s: %s; ", field, msg)
		}
		return fiber.NewError(fiber.StatusBadRequest, errMsg)
	}

	var sentTo string
	var medium domain.OtpMedium
	if req.Email != "" {
		sentTo = req.Email
		medium = domain.OtpMediumEmail
	} else if req.PhoneNumber != "" {
		sentTo = req.PhoneNumber
		medium = domain.OtpMediumSms
	} else {
		h.mongoLoggerSvc.Info("Email or PhoneNumber must be provided",
			zap.String("Email", req.Email),
			zap.String("Phone Number", req.PhoneNumber),
			zap.Int("status_code", fiber.StatusBadRequest),
			zap.Time("timestamp", time.Now()),
		)
		return fiber.NewError(fiber.StatusBadRequest, "Email or PhoneNumber must be provided")
	}

	if err := h.userSvc.SendResetCode(c.Context(), medium, sentTo, domain.AfroMessage); err != nil {
		h.mongoLoggerSvc.Error("Failed to send reset code",
			zap.String("medium", string(medium)),
			zap.String("sentTo", string(sentTo)),
			zap.Int("status_code", fiber.StatusInternalServerError),
			zap.Error(err),
			zap.Time("timestamp", time.Now()),
		)
		return fiber.NewError(fiber.StatusInternalServerError, "Failed to send reset code:"+err.Error())
	}

	return response.WriteJSON(c, fiber.StatusOK, "Code sent successfully", nil, nil)
}

type ResetPasswordReq struct {
	Email       string `json:"email" example:"john.doe@example.com"`
	PhoneNumber string `json:"phone_number" example:"1234567890"`
	Password    string `json:"password" validate:"required,min=8" example:"newpassword123"`
	Otp         string `json:"otp" validate:"required" example:"123456"`
}

// ResetPassword godoc
// @Summary Reset password
// @Description Reset password
// @Tags user
// @Accept json
// @Produce json
// @Param resetPassword body ResetPasswordReq true "Reset password"
// @Success 200 {object} response.APIResponse
// @Failure 400 {object} response.APIResponse
// @Failure 500 {object} response.APIResponse
// @Router /api/v1/user/resetPassword [post]
func (h *Handler) ResetPassword(c *fiber.Ctx) error {
	var req ResetPasswordReq
	if err := c.BodyParser(&req); err != nil {
		h.mongoLoggerSvc.Info("Failed to parse ResetPassword request",
			zap.Int("status_code", fiber.StatusBadRequest),
			zap.Error(err),
			zap.Time("timestamp", time.Now()),
		)
		return fiber.NewError(fiber.StatusBadRequest, "Invalid request body:"+err.Error())
	}

	if valErrs, ok := h.validator.Validate(c, req); !ok {
		var errMsg string
		for field, msg := range valErrs {
			errMsg += fmt.Sprintf("%s: %s; ", field, msg)
		}
		return fiber.NewError(fiber.StatusBadRequest, errMsg)
	}

	if req.Email == "" && req.PhoneNumber == "" {
		return fiber.NewError(fiber.StatusBadRequest, "Email or phone number is required")
	}

	resetReq := domain.ResetPasswordReq{
		Email:       req.Email,
		PhoneNumber: req.PhoneNumber,
		Password:    req.Password,
		OtpCode:     req.Otp,
	}

	if err := h.userSvc.ResetPassword(c.Context(), resetReq); err != nil {
		h.mongoLoggerSvc.Error("Failed to reset password",
			zap.String("email", req.Email),
			zap.String("phone", req.PhoneNumber),
			zap.Int("status_code", fiber.StatusInternalServerError),
			zap.Error(err),
			zap.Time("timestamp", time.Now()),
		)
		return fiber.NewError(fiber.StatusInternalServerError, "Failed to reset password:"+err.Error())
	}

	return response.WriteJSON(c, fiber.StatusOK, "Password reset successful", nil, nil)
}

// ResetTenantPassword godoc
// @Summary Reset tenant password
// @Description Reset tenant password
// @Tags user
// @Accept json
// @Produce json
// @Param resetPassword body ResetPasswordReq true "Reset password"
// @Success 200 {object} response.APIResponse
// @Failure 400 {object} response.APIResponse
// @Failure 500 {object} response.APIResponse
// @Router /api/v1/{tenant_slug}/user/resetPassword [post]
func (h *Handler) ResetTenantPassword(c *fiber.Ctx) error {
	// Reuse the main ResetPassword handler
	return h.ResetPassword(c)
}

// CustomerProfile godoc
// @Summary Get user profile
// @Description Get user profile
// @Tags user
// @Accept json
// @Produce json
// @Success 200 {object} domain.UserProfileResponse
// @Failure 400 {object} response.APIResponse
// @Failure 500 {object} response.APIResponse
// @Security Bearer
// @Router /api/v1/{tenant_slug}/user/user-profile [get]
func (h *Handler) GetUserProfile(c *fiber.Ctx) error {

	userID, ok := c.Locals("user_id").(int64)
	if !ok || userID == 0 {
		h.mongoLoggerSvc.Error("Invalid user ID in context",
			zap.Int64("userID", userID),
			zap.Int("status_code", fiber.StatusInternalServerError),
			zap.Time("timestamp", time.Now()),
		)
		return fiber.NewError(fiber.StatusInternalServerError, "Invalid user identification")
	}

	user, err := h.userSvc.GetUserByID(c.Context(), userID)
	if err != nil {
		h.mongoLoggerSvc.Error("Failed to get user profile",
			zap.Int64("userID", userID),
			zap.Int("status_code", fiber.StatusInternalServerError),
			zap.Error(err),
			zap.Time("timestamp", time.Now()),
		)
		return fiber.NewError(fiber.StatusInternalServerError, "Failed to retrieve user profile:"+err.Error())
	}

	lastLogin, err := h.authSvc.GetLastLogin(c.Context(), user.ID)
	if err != nil {
		if err != authentication.ErrRefreshTokenNotFound {
			h.mongoLoggerSvc.Error("Failed to get user last login",
				zap.Int64("userID", userID),
				zap.Int("status_code", fiber.StatusInternalServerError),
				zap.Error(err),
				zap.Time("timestamp", time.Now()),
			)
			return fiber.NewError(fiber.StatusInternalServerError, "Failed to retrieve user last login:"+err.Error())
		}

		lastLogin = &user.CreatedAt
	}

	res := domain.UserProfileResponse{
		ID:        user.ID,
		FirstName: user.FirstName,
		LastName:  user.LastName,
		// UserName:          user.UserName,
		Occupation:  user.Occupation,
		Email:       user.Email,
		PhoneNumber: user.PhoneNumber,
		Role:        user.Role,
		AgeGroup:    user.AgeGroup,
		BirthDay: func() string {
			if !user.BirthDay.IsZero() {
				return user.BirthDay.Format("2006-01-02")
			}
			return ""
		}(),
		EducationLevel:    user.EducationLevel,
		Country:           user.Country,
		Region:            user.Region,
		EmailVerified:     user.EmailVerified,
		PhoneVerified:     user.PhoneVerified,
		Status:            user.Status,
		LastLogin:         lastLogin,
		ProfileCompleted:  user.ProfileCompleted,
		ProfilePictureURL: user.ProfilePictureURL,
		PreferredLanguage: user.PreferredLanguage,
		CreatedAt:         user.CreatedAt,
		UpdatedAt:         user.UpdatedAt,
	}
	return response.WriteJSON(c, fiber.StatusOK, "User profile retrieved successfully", res, nil)
}

type AdminProfileRes struct {
	ID            int64       `json:"id"`
	FirstName     string      `json:"first_name"`
	LastName      string      `json:"last_name"`
	Email         string      `json:"email"`
	PhoneNumber   string      `json:"phone_number"`
	Role          domain.Role `json:"role"`
	EmailVerified bool        `json:"email_verified"`
	PhoneVerified bool        `json:"phone_verified"`
	CreatedAt     time.Time   `json:"created_at"`
	UpdatedAt     time.Time   `json:"updated_at"`
	LastLogin     time.Time   `json:"last_login"`
	SuspendedAt   time.Time   `json:"suspended_at"`
	Suspended     bool        `json:"suspended"`
}

// AdminProfile godoc
// @Summary Get user profile
// @Description Get user profile
// @Tags user
// @Accept json
// @Produce json
// @Success 200 {object} AdminProfileRes
// @Failure 400 {object} response.APIResponse
// @Failure 500 {object} response.APIResponse
// @Security Bearer
// @Router /api/v1/{tenant_slug}/user/admin-profile [get]
func (h *Handler) AdminProfile(c *fiber.Ctx) error {

	userID, ok := c.Locals("user_id").(int64)
	if !ok || userID == 0 {
		h.mongoLoggerSvc.Error("Invalid user ID in context",
			zap.Int64("userID", userID),
			zap.Int("status_code", fiber.StatusInternalServerError),
			zap.Time("timestamp", time.Now()),
		)
		return fiber.NewError(fiber.StatusInternalServerError, "Invalid user identification")
	}

	user, err := h.userSvc.GetUserByID(c.Context(), userID)
	if err != nil {
		h.mongoLoggerSvc.Error("Failed to get user profile",
			zap.Int64("userID", userID),
			zap.Int("status_code", fiber.StatusInternalServerError),
			zap.Error(err),
			zap.Time("timestamp", time.Now()),
		)
		return fiber.NewError(fiber.StatusInternalServerError, "Failed to retrieve user profile:"+err.Error())
	}

	lastLogin, err := h.authSvc.GetLastLogin(c.Context(), user.ID)
	if err != nil {
		if err != authentication.ErrRefreshTokenNotFound {
			h.mongoLoggerSvc.Error("Failed to get user last login",
				zap.Int64("userID", userID),
				zap.Int("status_code", fiber.StatusInternalServerError),
				zap.Error(err),
				zap.Time("timestamp", time.Now()),
			)
			return fiber.NewError(fiber.StatusInternalServerError, "Failed to retrieve user last login:"+err.Error())
		}

		lastLogin = &user.CreatedAt
	}
	res := domain.UserProfileResponse{
		ID:        user.ID,
		FirstName: user.FirstName,
		LastName:  user.LastName,
		// UserName:          user.UserName,
		Email:             user.Email,
		PhoneNumber:       user.PhoneNumber,
		Role:              user.Role,
		AgeGroup:          user.AgeGroup,
		EducationLevel:    user.EducationLevel,
		Country:           user.Country,
		Region:            user.Region,
		EmailVerified:     user.EmailVerified,
		PhoneVerified:     user.PhoneVerified,
		Status:            user.Status,
		LastLogin:         lastLogin,
		ProfileCompleted:  user.ProfileCompleted,
		ProfilePictureURL: user.ProfilePictureURL,
		PreferredLanguage: user.PreferredLanguage,
		CreatedAt:         user.CreatedAt,
		UpdatedAt:         user.UpdatedAt,
	}
	// Ensure birthday is included and formatted
	if !user.BirthDay.IsZero() {
		res.BirthDay = user.BirthDay.Format("2006-01-02")
	}

	return response.WriteJSON(c, fiber.StatusOK, "User profile retrieved successfully", res, nil)
}

// Helper function (unchanged)
func getMedium(email, phoneNumber string) (domain.OtpMedium, error) {
	if email != "" {
		return domain.OtpMediumEmail, nil
	}
	if phoneNumber != "" {
		return domain.OtpMediumSms, nil
	}
	return "", errors.New("both email and phone number are empty")
}

type SearchUserByNameOrPhoneReq struct {
	SearchString string       `json:"query"`
	Role         *domain.Role `json:"role,omitempty"`
}

// SearchUserByNameOrPhone godoc
// @Summary Search for user using name or phone
// @Description Search for user using name or phone
// @Tags user
// @Accept json
// @Produce json
// @Param searchUserByNameOrPhone body SearchUserByNameOrPhoneReq true "Search for using his name or phone"
// @Success 200 {object} domain.UserProfileResponse
// @Failure 400 {object} response.APIResponse
// @Failure 500 {object} response.APIResponse
// @Router /api/v1/user/search [post]
func (h *Handler) SearchUserByNameOrPhone(c *fiber.Ctx) error {
	var req SearchUserByNameOrPhoneReq
	if err := c.BodyParser(&req); err != nil {
		h.mongoLoggerSvc.Error("SearchUserByNameOrPhone failed - invalid request body",
			zap.Any("request", req),
			zap.Int("status_code", fiber.StatusBadRequest),
			zap.Error(err),
			zap.Time("timestamp", time.Now()),
		)
		return fiber.NewError(fiber.StatusBadRequest, "Invalid request body: "+err.Error())
	}

	if valErrs, ok := h.validator.Validate(c, req); !ok {
		var errMsg string
		for field, msg := range valErrs {
			errMsg += fmt.Sprintf("%s: %s; ", field, msg)
		}
		return fiber.NewError(fiber.StatusBadRequest, errMsg)
	}

	// Optional role filter
	var rolePtr *int64
	if req.Role != nil && *req.Role != "" {
		roleStr := string(*req.Role)
		roleVal, err := strconv.ParseInt(roleStr, 10, 64)
		if err != nil {
			h.mongoLoggerSvc.Info("Invalid role value",
				zap.String("role", roleStr),
				zap.Int("status_code", fiber.StatusBadRequest),
				zap.Error(err),
				zap.Time("timestamp", time.Now()),
			)
			return fiber.NewError(fiber.StatusBadRequest, "invalid role value")
		}
		rolePtr = &roleVal
	}

	users, err := h.userSvc.SearchUserByNameOrPhone(c.Context(), req.SearchString, rolePtr)
	if err != nil {
		h.mongoLoggerSvc.Error("SearchUserByNameOrPhone - failed to fetch users",
			zap.Any("request", req),
			zap.Int("status_code", fiber.StatusInternalServerError),
			zap.Error(err),
			zap.Time("timestamp", time.Now()),
		)
		return fiber.NewError(fiber.StatusInternalServerError, "Failed to get users: "+err.Error())
	}

	res := make([]domain.UserProfileResponse, 0, len(users))
	for _, user := range users {
		lastLogin, err := h.authSvc.GetLastLogin(c.Context(), user.ID)
		if err != nil && err != authentication.ErrRefreshTokenNotFound {
			h.mongoLoggerSvc.Error("Failed to get user last login",
				zap.Int64("user_id", user.ID),
				zap.Int("status_code", fiber.StatusInternalServerError),
				zap.Error(err),
				zap.Time("timestamp", time.Now()),
			)
			return fiber.NewError(fiber.StatusInternalServerError, "Failed to retrieve user last login: "+err.Error())
		}
		if err == authentication.ErrRefreshTokenNotFound {
			lastLogin = &user.CreatedAt
		}

		// var orgID *int64
		// if user.OrganizationID.Valid {
		// 	orgID = &user.OrganizationID.Value
		// }

		res = append(res, domain.UserProfileResponse{
			ID:        user.ID,
			FirstName: user.FirstName,
			LastName:  user.LastName,
			// UserName:          user.UserName,
			Email: user.Email,
			BirthDay: func() string {
				if !user.BirthDay.IsZero() {
					return user.BirthDay.Format("2006-01-02")
				}
				return ""
			}(),
			PhoneNumber:       user.PhoneNumber,
			Role:              user.Role,
			AgeGroup:          user.AgeGroup,
			EducationLevel:    user.EducationLevel,
			Country:           user.Country,
			Region:            user.Region,
			EmailVerified:     user.EmailVerified,
			PhoneVerified:     user.PhoneVerified,
			Status:            user.Status,
			LastLogin:         lastLogin,
			ProfileCompleted:  user.ProfileCompleted,
			ProfilePictureURL: user.ProfilePictureURL,
			PreferredLanguage: user.PreferredLanguage,
			CreatedAt:         user.CreatedAt,
			UpdatedAt:         user.UpdatedAt,
		})
	}

	return response.WriteJSON(c, fiber.StatusOK, "Search successful", res, nil)
}

// GetUserByID godoc
// @Summary Get user by id
// @Description Get a single user by id
// @Tags user
// @Accept json
// @Produce json
// @Param id path int true "User ID"
// @Success 200 {object} domain.UserProfileResponse
// @Failure 400 {object} response.APIResponse
// @Failure 401 {object} response.APIResponse
// @Failure 500 {object} response.APIResponse
// @Router /api/v1/user/single/{id} [get]
func (h *Handler) GetUserByID(c *fiber.Ctx) error {
	userIDstr := c.Params("id")
	userID, err := strconv.ParseInt(userIDstr, 10, 64)
	if err != nil {
		h.mongoLoggerSvc.Info("failed to parse user id",
			zap.String("userID", userIDstr),
			zap.Int("status_code", fiber.StatusBadRequest),
			zap.Error(err),
			zap.Time("timestamp", time.Now()),
		)
		return fiber.NewError(fiber.StatusBadRequest, "invalid user id")
	}

	user, err := h.userSvc.GetUserByID(c.Context(), userID)
	if err != nil {
		h.mongoLoggerSvc.Error("Get User By ID failed",
			zap.Int64("userID", userID),
			zap.Int("status_code", fiber.StatusInternalServerError),
			zap.Error(err),
			zap.Time("timestamp", time.Now()),
		)
		return fiber.NewError(fiber.StatusInternalServerError, "Failed to get user: "+err.Error())
	}

	lastLogin, err := h.authSvc.GetLastLogin(c.Context(), user.ID)
	if err != nil && err != authentication.ErrRefreshTokenNotFound {
		h.mongoLoggerSvc.Error("Failed to get user last login",
			zap.Int64("userID", user.ID),
			zap.Int("status_code", fiber.StatusInternalServerError),
			zap.Error(err),
			zap.Time("timestamp", time.Now()),
		)
		return fiber.NewError(fiber.StatusInternalServerError, "Failed to retrieve user last login: "+err.Error())
	}
	if err == authentication.ErrRefreshTokenNotFound {
		lastLogin = &user.CreatedAt
	}

	// var orgID *int64
	// if user.OrganizationID.Valid {
	// 	orgID = &user.OrganizationID.Value
	// }

	res := domain.UserProfileResponse{
		ID:        user.ID,
		FirstName: user.FirstName,
		LastName:  user.LastName,
		BirthDay: func() string {
			if !user.BirthDay.IsZero() {
				return user.BirthDay.Format("2006-01-02")
			}
			return ""
		}(),
		LearningGoal:               user.LearningGoal,
		LanguageGoal:               user.LanguageGoal,
		LanguageChallange:          user.LanguageChallange,
		Gender:                     user.Gender,
		InitialAssessmentCompleted: user.InitialAssessmentCompleted,
		NickName:                   user.NickName,
		Occupation:                 user.Occupation,
		FavouriteTopic:             user.FavouriteTopic,
		// UserName:          user.UserName,
		Email:             user.Email,
		PhoneNumber:       user.PhoneNumber,
		Role:              user.Role,
		AgeGroup:          user.AgeGroup,
		EducationLevel:    user.EducationLevel,
		Country:           user.Country,
		Region:            user.Region,
		EmailVerified:     user.EmailVerified,
		PhoneVerified:     user.PhoneVerified,
		Status:            user.Status,
		LastLogin:         lastLogin,
		ProfileCompleted:  user.ProfileCompleted,
		ProfilePictureURL: user.ProfilePictureURL,
		PreferredLanguage: user.PreferredLanguage,
		CreatedAt:         user.CreatedAt,
		UpdatedAt:         user.UpdatedAt,
	}

	return response.WriteJSON(c, fiber.StatusOK, "User retrieved successfully", res, nil)
}

// DeleteUser godoc
// @Summary Delete user by ID
// @Description Delete a user by their ID
// @Tags user
// @Accept json
// @Produce json
// @Param id path int true "User ID"
// @Success 200 {object} response.APIResponse
// @Failure 400 {object} response.APIResponse
// @Failure 500 {object} response.APIResponse
// @Router /api/v1/user/delete/{id} [delete]
func (h *Handler) DeleteUser(c *fiber.Ctx) error {
	userIDstr := c.Params("id")
	userID, err := strconv.ParseInt(userIDstr, 10, 64)
	if err != nil {
		h.mongoLoggerSvc.Info("Failed to parse user id",
			zap.String("userID", userIDstr),
			zap.Int("status_code", fiber.StatusBadRequest),
			zap.Error(err),
			zap.Time("timestamp", time.Now()),
		)
		return fiber.NewError(fiber.StatusBadRequest, "Invalid user ID")
	}

	err = h.userSvc.DeleteUser(c.Context(), userID)
	if err != nil {
		h.mongoLoggerSvc.Error("Failed to delete user",
			zap.Int64("userID", userID),
			zap.Int("status_code", fiber.StatusInternalServerError),
			zap.Error(err),
			zap.Time("timestamp", time.Now()),
		)
		return fiber.NewError(fiber.StatusInternalServerError, "Failed to delete user:"+err.Error())
	}

	actorID := c.Locals("user_id").(int64)
	actorRole := string(c.Locals("role").(domain.Role))
	ip := c.IP()
	ua := c.Get("User-Agent")
	meta, _ := json.Marshal(map[string]interface{}{"deleted_user_id": userID})
	go h.activityLogSvc.RecordAction(context.Background(), &actorID, &actorRole, domain.ActionUserDeleted, domain.ResourceUser, &userID, fmt.Sprintf("Deleted user ID: %d", userID), meta, &ip, &ua)

	go func() {
		admins, _, err := h.userSvc.GetAllUsers(context.Background(), domain.UserFilter{Role: string(domain.RoleAdmin)})
		if err != nil {
			return
		}
		for _, admin := range admins {
			if admin.ID != actorID {
				h.sendInAppNotification(admin.ID, domain.NOTIFICATION_TYPE_USER_DELETED, "User Deleted", fmt.Sprintf("User ID %d has been deleted.", userID))
			}
		}
	}()

	return response.WriteJSON(c, fiber.StatusOK, "User deleted successfully", nil, nil)
}

type UpdateUserSuspendReq struct {
	UserID    int64 `json:"user_id" validate:"required" example:"123"`
	Suspended bool  `json:"suspended" example:"true"`
}
type UpdateUserSuspendRes struct {
	UserID    int64 `json:"user_id"`
	Suspended bool  `json:"suspended"`
}

// UploadProfilePicture godoc
// @Summary Upload profile picture
// @Description Uploads a profile picture for the specified user
// @Tags user
// @Accept multipart/form-data
// @Produce json
// @Param id path int true "User ID"
// @Param file formData file true "Image file (jpg|png|webp)"
// @Success 200 {object} response.APIResponse
// @Failure 400 {object} response.APIResponse
// @Failure 500 {object} response.APIResponse
// @Security Bearer
// @Router /api/v1/user/{id}/profile-picture [post]
func (h *Handler) UploadProfilePicture(c *fiber.Ctx) error {
	idStr := c.Params("id")
	userID, err := strconv.ParseInt(idStr, 10, 64)
	if err != nil || userID <= 0 {
		return c.Status(fiber.StatusBadRequest).JSON(domain.ErrorResponse{
			Message: "Invalid user id",
			Error:   "User id must be a positive integer",
		})
	}

	fileHeader, err := c.FormFile("file")
	if err != nil {
		return c.Status(fiber.StatusBadRequest).JSON(domain.ErrorResponse{
			Message: "Invalid file",
			Error:   err.Error(),
		})
	}

	const maxSize = 5 * 1024 * 1024 // 5 MB
	if fileHeader.Size > maxSize {
		return c.Status(fiber.StatusBadRequest).JSON(domain.ErrorResponse{
			Message: "File too large",
			Error:   "Profile picture must be <= 5MB",
		})
	}

	fh, err := fileHeader.Open()
	if err != nil {
		return c.Status(fiber.StatusInternalServerError).JSON(domain.ErrorResponse{
			Message: "Failed to read file",
			Error:   err.Error(),
		})
	}
	defer fh.Close()

	// Read up to first 512 bytes for content sniffing, then read the rest
	head := make([]byte, 512)
	n, _ := fh.Read(head)
	contentType := http.DetectContentType(head[:n])
	if contentType != "image/jpeg" && contentType != "image/png" && contentType != "image/webp" {
		return c.Status(fiber.StatusBadRequest).JSON(domain.ErrorResponse{
			Message: "Invalid file type",
			Error:   "Only jpg, png and webp images are allowed",
		})
	}

	// Read remaining bytes
	rest, err := io.ReadAll(fh)
	if err != nil {
		return c.Status(fiber.StatusInternalServerError).JSON(domain.ErrorResponse{
			Message: "Failed to read file",
			Error:   err.Error(),
		})
	}

	// Combine head + rest
	data := append(head[:n], rest...)

	ext := ".jpg"
	switch contentType {
	case "image/png":
		ext = ".png"
	case "image/webp":
		ext = ".webp"
	}

	// Ensure directory exists
	dir := filepath.Join(".", "static", "profile_pictures")
	if err := os.MkdirAll(dir, 0o755); err != nil {
		return c.Status(fiber.StatusInternalServerError).JSON(domain.ErrorResponse{
			Message: "Failed to create storage directory",
			Error:   err.Error(),
		})
	}

	filename := uuid.New().String() + ext
	fullpath := filepath.Join(dir, filename)

	if err := os.WriteFile(fullpath, data, 0o644); err != nil {
		return c.Status(fiber.StatusInternalServerError).JSON(domain.ErrorResponse{
			Message: "Failed to save file",
			Error:   err.Error(),
		})
	}

	publicPath := "/static/profile_pictures/" + filename

	// Update user profile picture URL
	req := domain.UpdateUserReq{
		UserID:            userID,
		ProfilePictureURL: publicPath,
	}

	if err := h.userSvc.UpdateUser(c.Context(), req); err != nil {
		// Attempt to remove file on failure
		_ = os.Remove(fullpath)
		h.mongoLoggerSvc.Error("Failed to update user with profile picture",
			zap.Int64("user_id", userID),
			zap.Error(err),
			zap.Time("timestamp", time.Now()),
		)
		return c.Status(fiber.StatusInternalServerError).JSON(domain.ErrorResponse{
			Message: "Failed to update user",
			Error:   err.Error(),
		})
	}

	return c.Status(fiber.StatusOK).JSON(domain.Response{
		Message:    "Profile picture uploaded successfully",
		Data:       map[string]string{"profile_picture_url": publicPath},
		Success:    true,
		StatusCode: fiber.StatusOK,
	})

	// return response.WriteJSON(c, fiber.StatusOK, "Profile picture uploaded successfully", map[string]string{"profile_picture_url": publicPath}, nil)
}
