package control

import (
	"context"
	"encoding/hex"
	"strings"
	"sync/atomic"

	"git.frostfs.info/TrueCloudLab/frostfs-node/internal/audit"
	control "git.frostfs.info/TrueCloudLab/frostfs-node/pkg/services/control/ir"
	"git.frostfs.info/TrueCloudLab/frostfs-node/pkg/util/logger"
	"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/api/refs"
	cid "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/container/id"
	"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/user"
)

var _ control.ControlServiceServer = (*auditService)(nil)

type auditService struct {
	next    *Server
	log     *logger.Logger
	enabled *atomic.Bool
}

func NewAuditService(next *Server, log *logger.Logger, enabled *atomic.Bool) control.ControlServiceServer {
	return &auditService{
		next:    next,
		log:     log,
		enabled: enabled,
	}
}

// HealthCheck implements control.ControlServiceServer.
func (a *auditService) HealthCheck(ctx context.Context, req *control.HealthCheckRequest) (*control.HealthCheckResponse, error) {
	res, err := a.next.HealthCheck(ctx, req)
	if !a.enabled.Load() {
		return res, err
	}
	audit.LogRequestWithKey(ctx, a.log, control.ControlService_HealthCheck_FullMethodName, req.GetSignature().GetKey(), nil, err == nil)
	return res, err
}

// RemoveContainer implements control.ControlServiceServer.
func (a *auditService) RemoveContainer(ctx context.Context, req *control.RemoveContainerRequest) (*control.RemoveContainerResponse, error) {
	res, err := a.next.RemoveContainer(ctx, req)
	if !a.enabled.Load() {
		return res, err
	}

	sb := &strings.Builder{}
	var withConatiner bool
	if len(req.GetBody().GetContainerId()) > 0 {
		withConatiner = true
		sb.WriteString("containerID:")
		var containerID cid.ID
		if err := containerID.Decode(req.GetBody().GetContainerId()); err != nil {
			sb.WriteString(audit.InvalidValue)
		} else {
			sb.WriteString(containerID.EncodeToString())
		}
	}

	if len(req.GetBody().GetOwner()) > 0 {
		if withConatiner {
			sb.WriteString(";")
		}
		sb.WriteString("owner:")

		var ownerID refs.OwnerID
		if err := ownerID.Unmarshal(req.GetBody().GetOwner()); err != nil {
			sb.WriteString(audit.InvalidValue)
		} else {
			var owner user.ID
			if err := owner.ReadFromV2(ownerID); err != nil {
				sb.WriteString(audit.InvalidValue)
			} else {
				sb.WriteString(owner.EncodeToString())
			}
		}
	}

	audit.LogRequestWithKey(ctx, a.log, control.ControlService_RemoveContainer_FullMethodName, req.GetSignature().GetKey(), sb, err == nil)
	return res, err
}

// RemoveNode implements control.ControlServiceServer.
func (a *auditService) RemoveNode(ctx context.Context, req *control.RemoveNodeRequest) (*control.RemoveNodeResponse, error) {
	res, err := a.next.RemoveNode(ctx, req)
	if !a.enabled.Load() {
		return res, err
	}

	audit.LogRequestWithKey(ctx, a.log, control.ControlService_RemoveNode_FullMethodName, req.GetSignature().GetKey(),
		audit.TargetFromString(hex.EncodeToString(req.GetBody().GetKey())), err == nil)
	return res, err
}

// TickEpoch implements control.ControlServiceServer.
func (a *auditService) TickEpoch(ctx context.Context, req *control.TickEpochRequest) (*control.TickEpochResponse, error) {
	res, err := a.next.TickEpoch(ctx, req)
	if !a.enabled.Load() {
		return res, err
	}

	audit.LogRequestWithKey(ctx, a.log, control.ControlService_TickEpoch_FullMethodName, req.GetSignature().GetKey(),
		nil, err == nil)
	return res, err
}