package api

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

	"archivmail/internal/audit"
	"archivmail/internal/userstore"
)

func (s *Server) handleListUsers(w http.ResponseWriter, r *http.Request) {
	tenantID := tenantFromCtx(r.Context())

	var (
		users []*userstore.User
		err   error
	)
	if tenantID != nil {
		users, err = s.users.ListByTenant(r.Context(), *tenantID)
	} else {
		users, err = s.users.List("")
	}
	if err != nil {
		writeError(w, http.StatusInternalServerError, "failed to list users")
		return
	}

	type userResp struct {
		ID       int64  `json:"id"`
		Username string `json:"username"`
		Email    string `json:"email"`
		Role     string `json:"role"`
		Active   bool   `json:"active"`
		TenantID *int64 `json:"tenant_id,omitempty"`
	}

	resp := make([]userResp, 0, len(users))
	for _, u := range users {
		resp = append(resp, userResp{
			ID:       u.ID,
			Username: u.Username,
			Email:    u.Email,
			Role:     u.Role,
			Active:   u.Active,
			TenantID: u.TenantID,
		})
	}
	writeJSON(w, http.StatusOK, resp)
}

func (s *Server) handleCreateUser(w http.ResponseWriter, r *http.Request) {
	var req struct {
		Username string `json:"username"`
		Email    string `json:"email"`
		Password string `json:"password"`
		Role     string `json:"role"`
	}
	if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
		writeError(w, http.StatusBadRequest, "invalid request body")
		return
	}

	// SEC-01: Privilege escalation check — caller must not assign a role
	// at or above their own level.
	sess := sessionFromCtx(r.Context())
	if roleLevel(req.Role) >= roleLevel(sess.Role) {
		writeError(w, http.StatusForbidden, "insufficient privileges to assign this role")
		return
	}

	// SEC-02: Tenant isolation — non-superadmin users can only create users
	// within their own tenant.
	var tenantID *int64
	if sess.TenantID != nil {
		tenantID = sess.TenantID
	}

	// PROJ-29: Enforce max_users quota before creating a new user.
	if tenantID != nil && s.tenantStore != nil {
		quota, qErr := s.tenantStore.GetQuota(r.Context(), *tenantID)
		if qErr == nil && quota.MaxUsers != nil {
			usage, uErr := s.tenantStore.GetUsage(r.Context(), *tenantID)
			if uErr == nil && int(usage.UserCount) >= *quota.MaxUsers {
				writeError(w, http.StatusPaymentRequired, "user quota exceeded")
				return
			}
		}
	}

	user, err := s.users.Create(userstore.CreateUserRequest{
		Username: req.Username,
		Email:    req.Email,
		Password: req.Password,
		Role:     req.Role,
		TenantID: tenantID,
	})
	if err != nil {
		s.logger.Error("create user failed", "err", err)
		writeError(w, http.StatusBadRequest, "user creation failed")
		return
	}

	s.audlog.Log(audit.Entry{
		EventType: audit.EventUserMgmt,
		Username:  sess.Username,
		TenantID:  sess.TenantID,
		IPAddress: s.remoteIP(r),
		Detail:    "created user: " + user.Username,
		Success:   true,
	})

	writeJSON(w, http.StatusCreated, map[string]interface{}{
		"id":       user.ID,
		"username": user.Username,
		"email":    user.Email,
		"role":     user.Role,
		"active":   user.Active,
	})
}

func (s *Server) handleUpdateUser(w http.ResponseWriter, r *http.Request) {
	id, err := strconv.ParseInt(r.PathValue("id"), 10, 64)
	if err != nil {
		writeError(w, http.StatusBadRequest, "invalid user id")
		return
	}

	var req struct {
		Email    *string `json:"email"`
		Role     *string `json:"role"`
		Active   *bool   `json:"active"`
		Password *string `json:"password"`
	}
	if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
		writeError(w, http.StatusBadRequest, "invalid request body")
		return
	}

	sess := sessionFromCtx(r.Context())

	// SEC-02: Tenant isolation — load target user and verify same tenant.
	target, err := s.users.GetByID(id)
	if err != nil {
		writeError(w, http.StatusNotFound, "user not found")
		return
	}
	if sess.TenantID != nil {
		if target.TenantID == nil || *target.TenantID != *sess.TenantID {
			writeError(w, http.StatusForbidden, "access denied")
			return
		}
	}

	// SEC-01: Privilege escalation check — caller must not assign a role
	// at or above their own level, and must not modify users at or above
	// their own level.
	if roleLevel(target.Role) >= roleLevel(sess.Role) {
		writeError(w, http.StatusForbidden, "insufficient privileges to modify this user")
		return
	}
	if req.Role != nil && roleLevel(*req.Role) >= roleLevel(sess.Role) {
		writeError(w, http.StatusForbidden, "insufficient privileges to assign this role")
		return
	}

	updated, err := s.users.Update(id, userstore.UpdateUserRequest{
		Email:    req.Email,
		Role:     req.Role,
		Active:   req.Active,
		Password: req.Password,
	})
	if err != nil {
		s.logger.Error("update user failed", "err", err)
		writeError(w, http.StatusBadRequest, "user update failed")
		return
	}

	s.audlog.Log(audit.Entry{
		EventType: audit.EventUserMgmt,
		Username:  sess.Username,
		TenantID:  sess.TenantID,
		IPAddress: s.remoteIP(r),
		Detail:    fmt.Sprintf("updated user %d", id),
		Success:   true,
	})

	writeJSON(w, http.StatusOK, map[string]interface{}{
		"id":       updated.ID,
		"username": updated.Username,
		"email":    updated.Email,
		"role":     updated.Role,
		"active":   updated.Active,
	})
}

func (s *Server) handleDeleteUser(w http.ResponseWriter, r *http.Request) {
	id, err := strconv.ParseInt(r.PathValue("id"), 10, 64)
	if err != nil {
		writeError(w, http.StatusBadRequest, "invalid user id")
		return
	}

	// Fetch user info before deletion for audit log and IMAP cleanup
	target, err := s.users.GetByID(id)
	if err != nil {
		writeError(w, http.StatusNotFound, "user not found")
		return
	}

	// SEC-02: Tenant isolation — domain_admin can only delete users in their own tenant.
	sess := sessionFromCtx(r.Context())
	if sess.TenantID != nil {
		if target.TenantID == nil || *target.TenantID != *sess.TenantID {
			writeError(w, http.StatusForbidden, "access denied")
			return
		}
	}

	// SEC-01: Cannot delete users at or above own privilege level.
	if roleLevel(target.Role) >= roleLevel(sess.Role) {
		writeError(w, http.StatusForbidden, "insufficient privileges to delete this user")
		return
	}

	if err := s.users.DeleteSafe(id); err != nil {
		if err.Error() == "userstore: cannot delete last admin" {
			writeError(w, http.StatusConflict, "cannot delete the last active admin")
			return
		}
		s.logger.Error("delete user failed", "err", err)
		writeError(w, http.StatusInternalServerError, "user deletion failed")
		return
	}

	// Remove all IMAP accounts that belonged to this user
	imapDeleted := 0
	if s.imapStore != nil {
		if n, err := s.imapStore.DeleteByOwner(r.Context(), target.Username); err != nil {
			s.logger.Warn("delete user: could not remove IMAP accounts", "user", target.Username, "err", err)
		} else {
			imapDeleted = n
		}
	}

	s.audlog.Log(audit.Entry{
		EventType: audit.EventUserMgmt,
		Username:  sess.Username,
		TenantID:  sess.TenantID,
		IPAddress: s.remoteIP(r),
		Detail: fmt.Sprintf(
			"deleted user %d (%s, role=%s); %d IMAP account(s) removed; emails retained per GoBD",
			id, target.Username, target.Role, imapDeleted,
		),
		Success: true,
	})

	writeJSON(w, http.StatusOK, map[string]bool{"ok": true})
}
