package server

import (
	"context"
	"errors"
	"fmt"
	"strings"
	"time"

	authzextv1 "github.com/grafana/grafana/pkg/services/authz/proto/v1"
	"go.opentelemetry.io/otel/codes"
)

func (s *Server) Query(ctx context.Context, req *authzextv1.QueryRequest) (*authzextv1.QueryResponse, error) {
	ctx, span := s.tracer.Start(ctx, "server.Query")
	defer span.End()

	defer func(t time.Time) {
		s.metrics.requestDurationSeconds.WithLabelValues("server.Query", req.GetNamespace()).Observe(time.Since(t).Seconds())
	}(time.Now())

	res, err := s.query(ctx, req)
	if err != nil {
		span.RecordError(err)
		span.SetStatus(codes.Error, err.Error())
		s.logger.Error("failed to perform query request", "error", err, "namespace", req.GetNamespace())
		return nil, errors.New("failed to perform query request")
	}

	return res, nil
}

func (s *Server) query(ctx context.Context, req *authzextv1.QueryRequest) (*authzextv1.QueryResponse, error) {
	if err := authorize(ctx, req.GetNamespace(), s.cfg); err != nil {
		return nil, err
	}

	storeInf, err := s.getStoreInfo(ctx, req.Namespace)
	if err != nil {
		return nil, fmt.Errorf("failed to get openfga store: %w", err)
	}

	if req.Operation == nil {
		return nil, errors.New("operation cannot be nil")
	}

	switch op := req.Operation.Operation.(type) {
	case *authzextv1.QueryOperation_GetFolderParents:
		return s.queryFolderParents(ctx, storeInf, op.GetFolderParents)
	default:
		return nil, errors.New("unsupported query operation type")
	}
}

func (s *Server) queryFolderParents(ctx context.Context, store *storeInfo, req *authzextv1.GetFolderParentsQuery) (*authzextv1.QueryResponse, error) {
	ctx, span := s.tracer.Start(ctx, "server.queryFolderParents")
	defer span.End()

	if req.GetFolder() == "" {
		return nil, errors.New("folder UID cannot be empty")
	}

	// Get raw tuples from OpenFGA
	tuples, err := s.listFolderParents(ctx, store, req.GetFolder())
	if err != nil {
		return nil, fmt.Errorf("failed to list folder parents: %w", err)
	}

	// Extract parent UIDs from tuples (business logic now server-side)
	parentUIDs := make([]string, 0, len(tuples))
	for _, tuple := range tuples {
		// Extract UID from format "folder:UID" or "folder:UID#relation"
		userParts := strings.Split(tuple.Key.User, ":")
		if len(userParts) != 2 {
			return nil, fmt.Errorf("invalid user format: %s, expected format: folder:UID or folder:UID#relation", tuple.Key.User)
		}

		// Remove any relation part after #
		uidAndRelationParts := strings.Split(userParts[1], "#")
		if len(uidAndRelationParts) == 0 {
			return nil, fmt.Errorf("invalid user format: %s, expected format: folder:UID or folder:UID#relation", tuple.Key.User)
		}

		parentUIDs = append(parentUIDs, uidAndRelationParts[0])
	}

	return &authzextv1.QueryResponse{
		Result: &authzextv1.QueryResponse_FolderParents{
			FolderParents: &authzextv1.GetFolderParentsResult{
				ParentUids: parentUIDs,
			},
		},
	}, nil
}
