package handlers

import (
	"bytes"
	"errors"
	"net/http"
	"time"

	"github.com/gorilla/mux"
	"github.com/seaweedfs/seaweedfs/weed/admin/dash"
	"github.com/seaweedfs/seaweedfs/weed/admin/view/app"
	"github.com/seaweedfs/seaweedfs/weed/admin/view/layout"
	"github.com/seaweedfs/seaweedfs/weed/credential"
	"github.com/seaweedfs/seaweedfs/weed/glog"
)

func groupErrorToHTTPStatus(err error) int {
	if errors.Is(err, credential.ErrGroupNotFound) {
		return http.StatusNotFound
	}
	if errors.Is(err, credential.ErrGroupAlreadyExists) {
		return http.StatusConflict
	}
	if errors.Is(err, credential.ErrUserNotInGroup) {
		return http.StatusBadRequest
	}
	if errors.Is(err, credential.ErrPolicyNotAttached) {
		return http.StatusBadRequest
	}
	if errors.Is(err, credential.ErrUserNotFound) {
		return http.StatusNotFound
	}
	if errors.Is(err, credential.ErrPolicyNotFound) {
		return http.StatusNotFound
	}
	if errors.Is(err, credential.ErrGroupNotEmpty) {
		return http.StatusConflict
	}
	return http.StatusInternalServerError
}

type GroupHandlers struct {
	adminServer *dash.AdminServer
}

func NewGroupHandlers(adminServer *dash.AdminServer) *GroupHandlers {
	return &GroupHandlers{adminServer: adminServer}
}

func (h *GroupHandlers) ShowGroups(w http.ResponseWriter, r *http.Request) {
	data, err := h.getGroupsPageData(r)
	if err != nil {
		glog.Errorf("Failed to get groups data: %v", err)
		writeJSONError(w, http.StatusInternalServerError, "Failed to load groups: "+err.Error())
		return
	}

	var buf bytes.Buffer
	component := app.Groups(data)
	viewCtx := layout.NewViewContext(r, dash.UsernameFromContext(r.Context()), dash.CSRFTokenFromContext(r.Context()))
	layoutComponent := layout.Layout(viewCtx, component)
	if err := layoutComponent.Render(r.Context(), &buf); err != nil {
		glog.Errorf("Failed to render groups template: %v", err)
		w.WriteHeader(http.StatusInternalServerError)
		return
	}
	w.Header().Set("Content-Type", "text/html")
	_, _ = w.Write(buf.Bytes())
}

func (h *GroupHandlers) GetGroups(w http.ResponseWriter, r *http.Request) {
	groups, err := h.adminServer.GetGroups(r.Context())
	if err != nil {
		glog.Errorf("Failed to get groups: %v", err)
		writeJSONError(w, http.StatusInternalServerError, "Failed to get groups")
		return
	}
	writeJSON(w, http.StatusOK, map[string]interface{}{"groups": groups})
}

func (h *GroupHandlers) CreateGroup(w http.ResponseWriter, r *http.Request) {
	var req dash.CreateGroupRequest
	if err := decodeJSONBody(newJSONMaxReader(w, r), &req); err != nil {
		writeJSONError(w, http.StatusBadRequest, "Invalid request: "+err.Error())
		return
	}
	if req.Name == "" {
		writeJSONError(w, http.StatusBadRequest, "Group name is required")
		return
	}
	group, err := h.adminServer.CreateGroup(r.Context(), req.Name)
	if err != nil {
		glog.Errorf("Failed to create group: %v", err)
		writeJSONError(w, groupErrorToHTTPStatus(err), "Failed to create group: "+err.Error())
		return
	}
	writeJSON(w, http.StatusOK, group)
}

func (h *GroupHandlers) GetGroupDetails(w http.ResponseWriter, r *http.Request) {
	name := mux.Vars(r)["name"]
	group, err := h.adminServer.GetGroupDetails(r.Context(), name)
	if err != nil {
		glog.Errorf("Failed to get group details: %v", err)
		status := groupErrorToHTTPStatus(err)
		msg := "Failed to retrieve group"
		if status == http.StatusNotFound {
			msg = "Group not found"
		}
		writeJSONError(w, status, msg)
		return
	}
	writeJSON(w, http.StatusOK, group)
}

func (h *GroupHandlers) DeleteGroup(w http.ResponseWriter, r *http.Request) {
	name := mux.Vars(r)["name"]
	if err := h.adminServer.DeleteGroup(r.Context(), name); err != nil {
		glog.Errorf("Failed to delete group: %v", err)
		writeJSONError(w, groupErrorToHTTPStatus(err), "Failed to delete group: "+err.Error())
		return
	}
	writeJSON(w, http.StatusOK, map[string]string{"message": "Group deleted successfully"})
}

func (h *GroupHandlers) GetGroupMembers(w http.ResponseWriter, r *http.Request) {
	name := mux.Vars(r)["name"]
	group, err := h.adminServer.GetGroupDetails(r.Context(), name)
	if err != nil {
		writeJSONError(w, groupErrorToHTTPStatus(err), "Failed to get group: "+err.Error())
		return
	}
	writeJSON(w, http.StatusOK, map[string]interface{}{"members": group.Members})
}

func (h *GroupHandlers) AddGroupMember(w http.ResponseWriter, r *http.Request) {
	name := mux.Vars(r)["name"]
	var req struct {
		Username string `json:"username"`
	}
	if err := decodeJSONBody(newJSONMaxReader(w, r), &req); err != nil {
		writeJSONError(w, http.StatusBadRequest, "Invalid request: "+err.Error())
		return
	}
	if req.Username == "" {
		writeJSONError(w, http.StatusBadRequest, "Username is required")
		return
	}
	if err := h.adminServer.AddGroupMember(r.Context(), name, req.Username); err != nil {
		writeJSONError(w, groupErrorToHTTPStatus(err), "Failed to add member: "+err.Error())
		return
	}
	writeJSON(w, http.StatusOK, map[string]string{"message": "Member added successfully"})
}

func (h *GroupHandlers) RemoveGroupMember(w http.ResponseWriter, r *http.Request) {
	name := mux.Vars(r)["name"]
	username := mux.Vars(r)["username"]
	if err := h.adminServer.RemoveGroupMember(r.Context(), name, username); err != nil {
		writeJSONError(w, groupErrorToHTTPStatus(err), "Failed to remove member: "+err.Error())
		return
	}
	writeJSON(w, http.StatusOK, map[string]string{"message": "Member removed successfully"})
}

func (h *GroupHandlers) GetGroupPolicies(w http.ResponseWriter, r *http.Request) {
	name := mux.Vars(r)["name"]
	group, err := h.adminServer.GetGroupDetails(r.Context(), name)
	if err != nil {
		writeJSONError(w, groupErrorToHTTPStatus(err), "Failed to get group: "+err.Error())
		return
	}
	writeJSON(w, http.StatusOK, map[string]interface{}{"policies": group.PolicyNames})
}

func (h *GroupHandlers) AttachGroupPolicy(w http.ResponseWriter, r *http.Request) {
	name := mux.Vars(r)["name"]
	var req struct {
		PolicyName string `json:"policy_name"`
	}
	if err := decodeJSONBody(newJSONMaxReader(w, r), &req); err != nil {
		writeJSONError(w, http.StatusBadRequest, "Invalid request: "+err.Error())
		return
	}
	if req.PolicyName == "" {
		writeJSONError(w, http.StatusBadRequest, "Policy name is required")
		return
	}
	if err := h.adminServer.AttachGroupPolicy(r.Context(), name, req.PolicyName); err != nil {
		writeJSONError(w, groupErrorToHTTPStatus(err), "Failed to attach policy: "+err.Error())
		return
	}
	writeJSON(w, http.StatusOK, map[string]string{"message": "Policy attached successfully"})
}

func (h *GroupHandlers) DetachGroupPolicy(w http.ResponseWriter, r *http.Request) {
	name := mux.Vars(r)["name"]
	policyName := mux.Vars(r)["policyName"]
	if err := h.adminServer.DetachGroupPolicy(r.Context(), name, policyName); err != nil {
		writeJSONError(w, groupErrorToHTTPStatus(err), "Failed to detach policy: "+err.Error())
		return
	}
	writeJSON(w, http.StatusOK, map[string]string{"message": "Policy detached successfully"})
}

func (h *GroupHandlers) SetGroupStatus(w http.ResponseWriter, r *http.Request) {
	name := mux.Vars(r)["name"]
	var req struct {
		Enabled *bool `json:"enabled"`
	}
	if err := decodeJSONBody(newJSONMaxReader(w, r), &req); err != nil {
		writeJSONError(w, http.StatusBadRequest, "Invalid request: "+err.Error())
		return
	}
	if req.Enabled == nil {
		writeJSONError(w, http.StatusBadRequest, "enabled field is required")
		return
	}
	if err := h.adminServer.SetGroupStatus(r.Context(), name, *req.Enabled); err != nil {
		writeJSONError(w, groupErrorToHTTPStatus(err), "Failed to update group status: "+err.Error())
		return
	}
	writeJSON(w, http.StatusOK, map[string]string{"message": "Group status updated"})
}

func (h *GroupHandlers) getGroupsPageData(r *http.Request) (dash.GroupsPageData, error) {
	username := dash.UsernameFromContext(r.Context())
	if username == "" {
		username = "admin"
	}

	groups, err := h.adminServer.GetGroups(r.Context())
	if err != nil {
		return dash.GroupsPageData{}, err
	}

	activeCount := 0
	for _, g := range groups {
		if g.Status == "enabled" {
			activeCount++
		}
	}

	// Get available users for dropdown
	var availableUsers []string
	users, err := h.adminServer.GetObjectStoreUsers(r.Context())
	if err == nil {
		for _, user := range users {
			availableUsers = append(availableUsers, user.Username)
		}
	}

	// Get available policies for dropdown
	var availablePolicies []string
	policies, err := h.adminServer.GetPolicies()
	if err == nil {
		for _, p := range policies {
			availablePolicies = append(availablePolicies, p.Name)
		}
	}

	return dash.GroupsPageData{
		Username:          username,
		Groups:            groups,
		TotalGroups:       len(groups),
		ActiveGroups:      activeCount,
		AvailableUsers:    availableUsers,
		AvailablePolicies: availablePolicies,
		LastUpdated:       time.Now(),
	}, nil
}
