package commerce

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

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

	qmiddleware "qycw/go-server/internal/middleware"
	"qycw/go-server/internal/response"
)

type SecurityGroupHandler struct {
	service *Service
	auth    *qmiddleware.Auth
	logger  *slog.Logger
}

func NewSecurityGroupHandler(service *Service, auth *qmiddleware.Auth, logger *slog.Logger) *SecurityGroupHandler {
	return &SecurityGroupHandler{service: service, auth: auth, logger: logger}
}

func (h *SecurityGroupHandler) Routes() http.Handler {
	r := chi.NewRouter()
	r.Use(h.auth.RequireUser)
	r.Get("/", h.list)
	r.Post("/", h.create)
	r.Delete("/instance/{instance_id}/link", h.unlink)
	r.Post("/{security_group_id}/rules", h.createRule)
	r.Get("/{security_group_id}", h.detail)
	r.Put("/{security_group_id}", h.update)
	r.Delete("/{security_group_id}", h.delete)
	r.Post("/{security_group_id}/links", h.link)
	return r
}

func (h *SecurityGroupHandler) RuleRoutes() http.Handler {
	r := chi.NewRouter()
	r.Use(h.auth.RequireUser)
	r.Post("/{security_group_id}/rules", h.createRule)
	r.Put("/{rule_id}", h.updateRule)
	r.Delete("/{rule_id}", h.deleteRule)
	return r
}

func (h *SecurityGroupHandler) ProtocolRoutes() http.Handler {
	r := chi.NewRouter()
	r.Use(h.auth.RequireUser)
	r.Get("/", h.protocols)
	return r
}

func (h *SecurityGroupHandler) list(w http.ResponseWriter, r *http.Request) {
	userID, ok := h.userID(w, r)
	if !ok {
		return
	}
	data, err := h.service.ListSecurityGroups(r.Context(), userID, r.URL.Query())
	h.writeResult(w, data, err)
}

func (h *SecurityGroupHandler) detail(w http.ResponseWriter, r *http.Request) {
	userID, groupID, ok := h.userAndID(w, r, "security_group_id")
	if !ok {
		return
	}
	data, err := h.service.GetSecurityGroup(r.Context(), userID, groupID, r.URL.Query().Get("get_all_rule") != "0")
	h.writeResult(w, data, err)
}

func (h *SecurityGroupHandler) create(w http.ResponseWriter, r *http.Request) {
	userID, ok := h.userID(w, r)
	if !ok {
		return
	}
	var req SecurityGroupCreateRequest
	if !decodeSecurityGroupJSON(w, r, &req) {
		return
	}
	data, err := h.service.CreateSecurityGroup(r.Context(), userID, req)
	h.writeResult(w, data, err)
}

func (h *SecurityGroupHandler) update(w http.ResponseWriter, r *http.Request) {
	userID, groupID, ok := h.userAndID(w, r, "security_group_id")
	if !ok {
		return
	}
	var req SecurityGroupUpdateRequest
	if !decodeSecurityGroupJSON(w, r, &req) {
		return
	}
	data, err := h.service.UpdateSecurityGroup(r.Context(), userID, groupID, req)
	h.writeResult(w, data, err)
}

func (h *SecurityGroupHandler) delete(w http.ResponseWriter, r *http.Request) {
	userID, groupID, ok := h.userAndID(w, r, "security_group_id")
	if !ok {
		return
	}
	data, err := h.service.DeleteSecurityGroup(r.Context(), userID, groupID)
	h.writeResult(w, data, err)
}

func (h *SecurityGroupHandler) link(w http.ResponseWriter, r *http.Request) {
	userID, groupID, ok := h.userAndID(w, r, "security_group_id")
	if !ok {
		return
	}
	var req SecurityGroupLinkRequest
	if !decodeSecurityGroupJSON(w, r, &req) {
		return
	}
	data, err := h.service.LinkSecurityGroup(r.Context(), userID, groupID, req)
	h.writeResult(w, data, err)
}

func (h *SecurityGroupHandler) unlink(w http.ResponseWriter, r *http.Request) {
	userID, instanceID, ok := h.userAndID(w, r, "instance_id")
	if !ok {
		return
	}
	data, err := h.service.UnlinkSecurityGroup(r.Context(), userID, instanceID)
	h.writeResult(w, data, err)
}

func (h *SecurityGroupHandler) createRule(w http.ResponseWriter, r *http.Request) {
	userID, groupID, ok := h.userAndID(w, r, "security_group_id")
	if !ok {
		return
	}
	var req SecurityGroupRuleRequest
	if !decodeSecurityGroupJSON(w, r, &req) {
		return
	}
	data, err := h.service.CreateSecurityGroupRule(r.Context(), userID, groupID, req)
	h.writeResult(w, data, err)
}

func (h *SecurityGroupHandler) updateRule(w http.ResponseWriter, r *http.Request) {
	userID, ruleID, ok := h.userAndID(w, r, "rule_id")
	if !ok {
		return
	}
	var req SecurityGroupRuleRequest
	if !decodeSecurityGroupJSON(w, r, &req) {
		return
	}
	data, err := h.service.UpdateSecurityGroupRule(r.Context(), userID, ruleID, req)
	h.writeResult(w, data, err)
}

func (h *SecurityGroupHandler) deleteRule(w http.ResponseWriter, r *http.Request) {
	userID, ruleID, ok := h.userAndID(w, r, "rule_id")
	if !ok {
		return
	}
	data, err := h.service.DeleteSecurityGroupRule(r.Context(), userID, ruleID)
	h.writeResult(w, data, err)
}

func (h *SecurityGroupHandler) protocols(w http.ResponseWriter, r *http.Request) {
	userID, ok := h.userID(w, r)
	if !ok {
		return
	}
	data, err := h.service.ListSecurityGroupRuleProtocols(r.Context(), userID)
	h.writeResult(w, data, err)
}

func (h *SecurityGroupHandler) userAndID(w http.ResponseWriter, r *http.Request, param string) (int64, int64, bool) {
	userID, ok := h.userID(w, r)
	if !ok {
		return 0, 0, false
	}
	id, ok := parseID(w, chi.URLParam(r, param))
	if !ok {
		return 0, 0, false
	}
	return userID, id, true
}

func (h *SecurityGroupHandler) userID(w http.ResponseWriter, r *http.Request) (int64, bool) {
	principal, ok := qmiddleware.PrincipalFromContext(r.Context())
	if !ok || principal.ID <= 0 {
		response.Detail(w, http.StatusUnauthorized, "Could not validate credentials")
		return 0, false
	}
	return principal.ID, true
}

func (h *SecurityGroupHandler) writeResult(w http.ResponseWriter, data any, err error) {
	if err != nil {
		h.writeError(w, err)
		return
	}
	response.OK(w, data)
}

func (h *SecurityGroupHandler) writeError(w http.ResponseWriter, err error) {
	var httpErr HTTPError
	if errors.As(err, &httpErr) {
		response.Detail(w, httpErr.Status, httpErr.Detail)
		return
	}
	if h.logger != nil {
		h.logger.Error("security group handler error", "error", err)
	}
	response.Detail(w, http.StatusInternalServerError, msgServiceError)
}

func decodeSecurityGroupJSON(w http.ResponseWriter, r *http.Request, out any) bool {
	defer r.Body.Close()
	if err := json.NewDecoder(r.Body).Decode(out); err != nil {
		response.Detail(w, http.StatusBadRequest, msgInvalidParams)
		return false
	}
	return true
}
