package handlers

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

	"github.com/gorilla/sessions"
	"golang.org/x/crypto/bcrypt"
	"xorm.io/xorm"

	"github.com/forgeo/forgebucket/internal/api/middleware"
	"github.com/forgeo/forgebucket/internal/models"
)

type UserHandler struct {
	db    *xorm.Engine
	store sessions.Store
}

func NewUserHandler(db *xorm.Engine, store sessions.Store) *UserHandler {
	return &UserHandler{db: db, store: store}
}

func (h *UserHandler) Register(w http.ResponseWriter, r *http.Request) {
	var body struct {
		Username string `json:"username"`
		Email    string `json:"email"`
		Password string `json:"password"`
	}
	if err := json.NewDecoder(r.Body).Decode(&body); err != nil {
		jsonError(w, "invalid request body", http.StatusBadRequest)
		return
	}
	if body.Username == "" || body.Email == "" || body.Password == "" {
		jsonError(w, "username, email, and password are required", http.StatusBadRequest)
		return
	}

	hash, err := bcrypt.GenerateFromPassword([]byte(body.Password), bcrypt.DefaultCost)
	if err != nil {
		jsonError(w, "internal error", http.StatusInternalServerError)
		return
	}

	user := &models.User{
		Username:     body.Username,
		Email:        body.Email,
		PasswordHash: string(hash),
	}
	if _, err := h.db.Insert(user); err != nil {
		jsonError(w, "username or email already taken", http.StatusConflict)
		return
	}

	w.Header().Set("Content-Type", "application/json")
	w.WriteHeader(http.StatusCreated)
	json.NewEncoder(w).Encode(user)
}

func (h *UserHandler) Login(w http.ResponseWriter, r *http.Request) {
	var body struct {
		Username string `json:"username"`
		Password string `json:"password"`
	}
	if err := json.NewDecoder(r.Body).Decode(&body); err != nil {
		jsonError(w, "invalid request body", http.StatusBadRequest)
		return
	}

	var user models.User
	found, err := h.db.Where("username = ?", body.Username).Get(&user)
	if err != nil || !found {
		jsonError(w, "invalid credentials", http.StatusUnauthorized)
		return
	}

	if err := bcrypt.CompareHashAndPassword([]byte(user.PasswordHash), []byte(body.Password)); err != nil {
		jsonError(w, "invalid credentials", http.StatusUnauthorized)
		return
	}

	session, _ := h.store.Get(r, "fb_session")
	session.Values["userID"] = user.ID
	session.Values["username"] = user.Username
	session.Values["isAdmin"] = user.IsAdmin
	if err := session.Save(r, w); err != nil {
		jsonError(w, "could not save session", http.StatusInternalServerError)
		return
	}

	w.Header().Set("Content-Type", "application/json")
	json.NewEncoder(w).Encode(user)
}

func (h *UserHandler) Logout(w http.ResponseWriter, r *http.Request) {
	session, _ := h.store.Get(r, "fb_session")
	session.Options.MaxAge = -1
	session.Save(r, w)
	w.WriteHeader(http.StatusNoContent)
}

func (h *UserHandler) Me(w http.ResponseWriter, r *http.Request) {
	userID, ok := middleware.UserIDFromContext(r.Context())
	if !ok {
		jsonError(w, "unauthorized", http.StatusUnauthorized)
		return
	}

	var user models.User
	found, err := h.db.ID(userID).Get(&user)
	if err != nil || !found {
		jsonError(w, "user not found", http.StatusNotFound)
		return
	}

	w.Header().Set("Content-Type", "application/json")
	json.NewEncoder(w).Encode(user)
}
