package api

import (
	"database/sql"
	"net/http"
	"strings"

	"github.com/gin-gonic/gin"
	"golang.org/x/crypto/bcrypt"

	"github.com/rycroftapparel/workpulse-api/internal/httpapi"
)

type changePasswordBody struct {
	CurrentPassword string `json:"currentPassword"`
	NewPassword     string `json:"newPassword" binding:"required,min=8"`
}

// postMePassword changes the authenticated user's password (POST /me/password).
func (s *Server) postMePassword(c *gin.Context) {
	var body changePasswordBody
	if err := c.ShouldBindJSON(&body); err != nil {
		c.JSON(http.StatusBadRequest, httpapi.Fail("validation", httpapi.BindErrorMessage(err)))
		return
	}
	newPass := strings.TrimSpace(body.NewPassword)
	if len(newPass) < 8 {
		c.JSON(http.StatusBadRequest, httpapi.Fail("validation", "Password baru minimal 8 karakter."))
		return
	}

	uid := userID(c)
	ctx, cancel := s.ctx(c)
	defer cancel()

	var hash sql.NullString
	var isActive bool
	err := s.DB.QueryRowContext(ctx,
		`SELECT password_hash, is_active FROM users WHERE id = $1`, uid,
	).Scan(&hash, &isActive)
	if err == sql.ErrNoRows {
		c.JSON(http.StatusNotFound, httpapi.Fail("not_found", "user not found"))
		return
	}
	if err != nil {
		c.JSON(http.StatusInternalServerError, httpapi.Fail("db", err.Error()))
		return
	}
	if !isActive {
		c.JSON(http.StatusForbidden, httpapi.Fail("forbidden", "account is disabled"))
		return
	}

	hasPassword := hash.Valid && strings.TrimSpace(hash.String) != ""
	current := body.CurrentPassword

	if hasPassword {
		if strings.TrimSpace(current) == "" {
			c.JSON(http.StatusBadRequest, httpapi.Fail("validation", "Password saat ini wajib diisi."))
			return
		}
		if bcrypt.CompareHashAndPassword([]byte(hash.String), []byte(current)) != nil {
			c.JSON(http.StatusUnauthorized, httpapi.Fail("invalid_credentials", "Password saat ini salah."))
			return
		}
		if current == newPass {
			c.JSON(http.StatusBadRequest, httpapi.Fail("validation", "Password baru harus berbeda dari password saat ini."))
			return
		}
	}

	newHash, err := bcrypt.GenerateFromPassword([]byte(newPass), bcrypt.DefaultCost)
	if err != nil {
		c.JSON(http.StatusInternalServerError, httpapi.Fail("crypto", err.Error()))
		return
	}
	if _, err := s.DB.ExecContext(ctx, `UPDATE users SET password_hash = $1 WHERE id = $2`, string(newHash), uid); err != nil {
		c.JSON(http.StatusInternalServerError, httpapi.Fail("db", err.Error()))
		return
	}
	_ = s.revokeAllRefreshSessions(ctx, uid)

	if hasPassword {
		s.recordUserActivity(c, ctx, uid, "security.password_change", "Password changed", "Account password was updated", nil)
	} else {
		s.recordUserActivity(c, ctx, uid, "security.password_set", "Password set", "Local password was configured for this account", nil)
	}

	c.JSON(http.StatusOK, httpapi.OK(gin.H{"ok": true}))
}
