// Package api provides gRPC server implementation for Audit Service.
package api

import (
	"context"

	auditv1 "git.dcentral.systems/toolz/goplt/api/proto/generated/audit/v1"
	"git.dcentral.systems/toolz/goplt/services/audit/internal/service"
	"go.uber.org/zap"
	"google.golang.org/grpc/codes"
	"google.golang.org/grpc/status"
)

// Server implements the AuditService gRPC server.
type Server struct {
	auditv1.UnimplementedAuditServiceServer
	service *service.AuditService
	logger  *zap.Logger
}

// NewServer creates a new Audit Service gRPC server.
func NewServer(auditService *service.AuditService, logger *zap.Logger) *Server {
	return &Server{
		service: auditService,
		logger:  logger,
	}
}

// Record records an audit log entry.
func (s *Server) Record(ctx context.Context, req *auditv1.RecordRequest) (*auditv1.RecordResponse, error) {
	if req.Entry == nil {
		return nil, status.Error(codes.InvalidArgument, "entry is required")
	}

	entry := req.Entry

	// Convert proto entry to service entry
	serviceEntry := &service.AuditLogEntry{
		UserID:     entry.UserId,
		Action:     entry.Action,
		Resource:   entry.Resource,
		ResourceID: entry.ResourceId,
		IPAddress:  entry.IpAddress,
		UserAgent:  entry.UserAgent,
		Metadata:   entry.Metadata,
		Timestamp:  entry.Timestamp,
	}

	// Record the audit log
	if err := s.service.Record(ctx, serviceEntry); err != nil {
		s.logger.Error("Failed to record audit log",
			zap.Error(err),
			zap.String("user_id", entry.UserId),
			zap.String("action", entry.Action),
		)
		return nil, status.Errorf(codes.Internal, "failed to record audit log: %v", err)
	}

	return &auditv1.RecordResponse{
		Success: true,
	}, nil
}

// Query queries audit logs based on filters.
func (s *Server) Query(ctx context.Context, req *auditv1.QueryRequest) (*auditv1.QueryResponse, error) {
	// Convert proto filters to service filters
	filters := &service.AuditLogFilters{
		Limit:  int(req.Limit),
		Offset: int(req.Offset),
	}

	if req.UserId != nil {
		userID := *req.UserId
		filters.UserID = &userID
	}
	if req.Action != nil {
		action := *req.Action
		filters.Action = &action
	}
	if req.Resource != nil {
		resource := *req.Resource
		filters.Resource = &resource
	}
	if req.ResourceId != nil {
		resourceID := *req.ResourceId
		filters.ResourceID = &resourceID
	}
	if req.StartTime != nil {
		startTime := *req.StartTime
		filters.StartTime = &startTime
	}
	if req.EndTime != nil {
		endTime := *req.EndTime
		filters.EndTime = &endTime
	}

	// Query audit logs
	entries, err := s.service.Query(ctx, filters)
	if err != nil {
		s.logger.Error("Failed to query audit logs",
			zap.Error(err),
		)
		return nil, status.Errorf(codes.Internal, "failed to query audit logs: %v", err)
	}

	// Convert service entries to proto entries
	protoEntries := make([]*auditv1.AuditLogEntry, 0, len(entries))
	for _, entry := range entries {
		protoEntries = append(protoEntries, &auditv1.AuditLogEntry{
			UserId:     entry.UserID,
			Action:     entry.Action,
			Resource:   entry.Resource,
			ResourceId: entry.ResourceID,
			IpAddress:  entry.IPAddress,
			UserAgent:  entry.UserAgent,
			Metadata:   entry.Metadata,
			Timestamp:  entry.Timestamp,
		})
	}

	return &auditv1.QueryResponse{
		Entries: protoEntries,
		Total:   int32(len(protoEntries)), // Note: This is a simplified total, actual total would require a count query
	}, nil
}
