package policy

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

	"github.com/go-chi/chi/v5"
	"github.com/google/uuid"

	"nexavpn/backend/internal/apiutil"
	"nexavpn/backend/internal/audit"
	"nexavpn/backend/internal/requestctx"
)

type Handler struct {
	service *Service
	audit   *audit.Service
}

func NewHandler(service *Service, auditService *audit.Service) *Handler {
	return &Handler{service: service, audit: auditService}
}

func (h *Handler) List(w http.ResponseWriter, r *http.Request) {
	items, err := h.service.List(r.Context())
	if err != nil {
		apiutil.Error(w, http.StatusInternalServerError, "policies_list_failed", "unable to list policies")
		return
	}

	apiutil.JSON(w, http.StatusOK, items)
}

func (h *Handler) Create(w http.ResponseWriter, r *http.Request) {
	var input CreateRequest
	if err := json.NewDecoder(r.Body).Decode(&input); err != nil {
		apiutil.Error(w, http.StatusBadRequest, "invalid_json", "invalid request body")
		return
	}

	claims, ok := requestctx.ClaimsFromContext(r.Context())
	if !ok {
		apiutil.Error(w, http.StatusUnauthorized, "unauthorized", "missing auth claims")
		return
	}

	item, err := h.service.Create(r.Context(), claims.UserID, input)
	if err != nil {
		apiutil.Error(w, http.StatusInternalServerError, "policy_create_failed", "unable to create policy")
		return
	}

	_ = h.audit.Record(r.Context(), audit.Entry{
		ActorUserID: &claims.UserID,
		EntityType:  "policy",
		EntityID:    &item.ID,
		EventType:   "admin.policy.created",
		Status:      "success",
		Message:     "admin created policy",
		Metadata: map[string]any{
			"name": item.Name,
		},
	})
	apiutil.JSON(w, http.StatusCreated, item)
}

func (h *Handler) Update(w http.ResponseWriter, r *http.Request) {
	policyID, err := uuid.Parse(chi.URLParam(r, "id"))
	if err != nil {
		apiutil.Error(w, http.StatusBadRequest, "invalid_policy_id", "invalid policy id")
		return
	}

	var input UpdateRequest
	if err := json.NewDecoder(r.Body).Decode(&input); err != nil {
		apiutil.Error(w, http.StatusBadRequest, "invalid_json", "invalid request body")
		return
	}

	item, err := h.service.Update(r.Context(), policyID, input)
	if err != nil {
		apiutil.Error(w, http.StatusInternalServerError, "policy_update_failed", "unable to update policy")
		return
	}

	if claims, ok := requestctx.ClaimsFromContext(r.Context()); ok {
		_ = h.audit.Record(r.Context(), audit.Entry{
			ActorUserID: &claims.UserID,
			EntityType:  "policy",
			EntityID:    &policyID,
			EventType:   "admin.policy.updated",
			Status:      "success",
			Message:     "admin updated policy",
		})
	}
	apiutil.JSON(w, http.StatusOK, item)
}

func (h *Handler) Delete(w http.ResponseWriter, r *http.Request) {
	policyID, err := uuid.Parse(chi.URLParam(r, "id"))
	if err != nil {
		apiutil.Error(w, http.StatusBadRequest, "invalid_policy_id", "invalid policy id")
		return
	}
	if err := h.service.Delete(r.Context(), policyID); err != nil {
		apiutil.Error(w, http.StatusInternalServerError, "policy_delete_failed", "unable to delete policy")
		return
	}
	if claims, ok := requestctx.ClaimsFromContext(r.Context()); ok {
		_ = h.audit.Record(r.Context(), audit.Entry{
			ActorUserID: &claims.UserID,
			EntityType:  "policy",
			EntityID:    &policyID,
			EventType:   "admin.policy.deleted",
			Status:      "success",
			Message:     "admin deleted policy",
		})
	}
	apiutil.JSON(w, http.StatusOK, map[string]any{"ok": true})
}
