package admin

import (
	"encoding/json"
	"log"
	"net/http"

	"gitea.local/admin/hspguard/internal/repository"
	"gitea.local/admin/hspguard/internal/types"
	"gitea.local/admin/hspguard/internal/util"
	"gitea.local/admin/hspguard/internal/web"
	"github.com/go-chi/chi/v5"
	"github.com/google/uuid"
)

func (h *AdminHandler) GetUsers(w http.ResponseWriter, r *http.Request) {
	userId, ok := util.GetRequestUserId(r.Context())
	if !ok {
		web.Error(w, "failed to get user id from auth session", http.StatusInternalServerError)
		return
	}

	user, err := h.repo.FindUserId(r.Context(), uuid.MustParse(userId))
	if err != nil {
		web.Error(w, "failed to get access information", http.StatusUnauthorized)
		return
	}

	users, err := h.repo.FindAdminUsers(r.Context(), &user.ID)
	if err != nil {
		log.Println("ERR: Failed to query users from db:", err)
		web.Error(w, "failed to get all users", http.StatusInternalServerError)
		return
	}

	type Response struct {
		Items []types.UserDTO `json:"items"`
		Count int             `json:"count"`
	}

	var items []types.UserDTO

	for _, user := range users {
		items = append(items, types.NewUserDTO(&user))
	}

	encoder := json.NewEncoder(w)

	w.Header().Set("Content-Type", "application/json")

	if err := encoder.Encode(&Response{
		Items: items,
		Count: len(items),
	}); err != nil {
		web.Error(w, "failed to send response", http.StatusInternalServerError)
	}
}

func (h *AdminHandler) GetUser(w http.ResponseWriter, r *http.Request) {
	userId := chi.URLParam(r, "id")
	parsed, err := uuid.Parse(userId)
	if err != nil {
		web.Error(w, "user id provided is not a valid uuid", http.StatusBadRequest)
		return
	}

	user, err := h.repo.FindUserId(r.Context(), parsed)
	if err != nil {
		web.Error(w, "user with provided id not found", http.StatusNotFound)
		return
	}

	encoder := json.NewEncoder(w)

	w.Header().Set("Content-Type", "application/json")

	if err := encoder.Encode(types.NewUserDTO(&user)); err != nil {
		web.Error(w, "failed to encode user dto", http.StatusInternalServerError)
	}
}

type CreateUserRequest struct {
	Email    string `json:"email"`
	FullName string `json:"full_name"`
	Password string `json:"password"`
	IsAdmin  bool   `json:"is_admin"`
}

func (h *AdminHandler) CreateUser(w http.ResponseWriter, r *http.Request) {
	userId, ok := util.GetRequestUserId(r.Context())
	if !ok {
		web.Error(w, "failed to get user id from auth session", http.StatusInternalServerError)
		return
	}

	user, err := h.repo.FindUserId(r.Context(), uuid.MustParse(userId))
	if err != nil {
		web.Error(w, "failed to get access information", http.StatusUnauthorized)
		return
	}

	var req CreateUserRequest

	decoder := json.NewDecoder(r.Body)
	if err := decoder.Decode(&req); err != nil {
		web.Error(w, "invalid request body", http.StatusBadRequest)
		return
	}

	if req.Email == "" {
		web.Error(w, "email is required", http.StatusBadRequest)
		return
	}

	if req.FullName == "" {
		web.Error(w, "full name is required", http.StatusBadRequest)
		return
	}

	if req.Password == "" {
		web.Error(w, "password is required", http.StatusBadRequest)
		return
	}

	_, err = h.repo.FindUserEmail(r.Context(), req.Email)
	if err == nil {
		web.Error(w, "user with provided email already exists", http.StatusBadRequest)
		return
	}

	hash, err := util.HashPassword(req.Password)
	if err != nil {
		log.Println("ERR: Failed to hash password for new user:", err)
		web.Error(w, "failed to create user account", http.StatusInternalServerError)
		return
	}

	params := repository.InsertUserParams{
		Email:        req.Email,
		FullName:     req.FullName,
		PasswordHash: hash,
		IsAdmin:      false,
		CreatedBy:    &user.ID,
	}

	log.Println("INFO: params for user creation:", params)

	id, err := h.repo.InsertUser(r.Context(), params)
	if err != nil {
		log.Println("ERR: Failed to insert user into database:", err)
		web.Error(w, "failed to create user", http.StatusInternalServerError)
		return
	}

	type Response struct {
		ID string `json:"id"`
	}

	encoder := json.NewEncoder(w)
	if err := encoder.Encode(Response{
		ID: id.String(),
	}); err != nil {
		web.Error(w, "failed to encode response", http.StatusInternalServerError)
	}
}
