package graph

// This file will be automatically regenerated based on the schema, any resolver implementations
// will be copied through when generating and any unknown code will be moved to the end.
// Code generated by github.com/99designs/gqlgen version v0.17.57

import (
	"context"
	"database/sql"
	"encoding/json"
	"errors"
	"fmt"
	"pentagi/pkg/controller"
	"pentagi/pkg/database"
	"pentagi/pkg/database/converter"
	"pentagi/pkg/flowfiles"
	"pentagi/pkg/graph/model"
	"pentagi/pkg/providers"
	"pentagi/pkg/providers/pconfig"
	"pentagi/pkg/providers/provider"
	"pentagi/pkg/resources"
	"pentagi/pkg/server/auth"
	"pentagi/pkg/server/update"
	"pentagi/pkg/templates"
	"pentagi/pkg/templates/validator"
	"pentagi/pkg/version"
	"strings"
	"time"
	"unicode/utf8"

	"github.com/sirupsen/logrus"
)

// CreateFlow is the resolver for the createFlow field.
func (r *mutationResolver) CreateFlow(ctx context.Context, modelProvider string, input string, resourceIds []int64) (*model.Flow, error) {
	uid, _, err := validatePermission(ctx, "flows.create")
	if err != nil {
		return nil, err
	}

	r.Logger.WithFields(logrus.Fields{
		"uid":      uid,
		"provider": modelProvider,
		"input":    input[:min(len(input), 1000)],
	}).Debug("create flow")

	if modelProvider == "" {
		return nil, fmt.Errorf("model provider is required")
	}

	if input == "" {
		return nil, fmt.Errorf("user input is required")
	}

	var dbResources []database.UserResource
	if _, isAdmin, err := validatePermission(ctx, "resources.view"); err == nil {
		dbResources, err = validateUserResources(ctx, r.DB, uid, isAdmin, resourceIds)
		if err != nil {
			return nil, err
		}
	}

	prvname := provider.ProviderName(modelProvider)
	prv, err := r.ProvidersCtrl.GetProvider(ctx, prvname, uid)
	if err != nil {
		return nil, err
	}
	prvtype := prv.Type()

	flowID, err := r.Controller.CreateFlow(ctx, uid, input, prvname, prvtype, nil, dbResources)
	if err != nil {
		return nil, err
	}

	flow, err := r.DB.GetFlow(ctx, flowID)
	if err != nil {
		return nil, err
	}

	var containers []database.Container
	if _, _, err = validatePermission(ctx, "containers.view"); err == nil {
		containers, err = r.DB.GetFlowContainers(ctx, flowID)
		if err != nil {
			return nil, err
		}
	}

	return converter.ConvertFlow(flow, containers), nil
}

// PutUserInput is the resolver for the putUserInput field.
func (r *mutationResolver) PutUserInput(ctx context.Context, flowID int64, input string, modelProvider *string, resourceIds []int64) (model.ResultType, error) {
	uid, err := validatePermissionWithFlowID(ctx, "flows.edit", flowID, r.DB)
	if err != nil {
		return model.ResultTypeError, err
	}

	if input == "" {
		return model.ResultTypeError, fmt.Errorf("user input is required")
	}

	fields := logrus.Fields{
		"uid":  uid,
		"flow": flowID,
	}
	if modelProvider != nil {
		if *modelProvider == "" {
			fields["modelProvider"] = "empty"
		} else {
			fields["modelProvider"] = *modelProvider
		}
	} else {
		fields["modelProvider"] = "unknown"
	}

	r.Logger.WithFields(fields).Debug("put user input")

	var dbResources []database.UserResource
	if _, isAdmin, err := validatePermission(ctx, "resources.view"); err == nil {
		dbResources, err = validateUserResources(ctx, r.DB, uid, isAdmin, resourceIds)
		if err != nil {
			return model.ResultTypeError, err
		}
	}

	fw, err := r.Controller.GetFlow(ctx, flowID)
	if err != nil {
		return model.ResultTypeError, err
	}

	var prv provider.Provider
	if modelProvider != nil && *modelProvider != "" {
		name := provider.ProviderName(*modelProvider)
		prv, err = r.ProvidersCtrl.GetProvider(ctx, name, uid)
		if err != nil {
			return model.ResultTypeError, fmt.Errorf("failed to get provider '%s': %w", *modelProvider, err)
		}
	}

	if err := fw.PutInput(ctx, input, prv, dbResources); err != nil {
		return model.ResultTypeError, err
	}

	return model.ResultTypeSuccess, nil
}

// StopFlow is the resolver for the stopFlow field.
func (r *mutationResolver) StopFlow(ctx context.Context, flowID int64) (model.ResultType, error) {
	uid, err := validatePermissionWithFlowID(ctx, "flows.edit", flowID, r.DB)
	if err != nil {
		return model.ResultTypeError, err
	}

	r.Logger.WithFields(logrus.Fields{
		"uid":  uid,
		"flow": flowID,
	}).Debug("stop flow")

	if err := r.Controller.StopFlow(ctx, flowID); err != nil {
		return model.ResultTypeError, err
	}

	return model.ResultTypeSuccess, nil
}

// FinishFlow is the resolver for the finishFlow field.
func (r *mutationResolver) FinishFlow(ctx context.Context, flowID int64) (model.ResultType, error) {
	uid, err := validatePermissionWithFlowID(ctx, "flows.edit", flowID, r.DB)
	if err != nil {
		return model.ResultTypeError, err
	}

	r.Logger.WithFields(logrus.Fields{
		"uid":  uid,
		"flow": flowID,
	}).Debug("finish flow")

	err = r.Controller.FinishFlow(ctx, flowID)
	if err != nil {
		return model.ResultTypeError, err
	}

	return model.ResultTypeSuccess, nil
}

// DeleteFlow is the resolver for the deleteFlow field.
func (r *mutationResolver) DeleteFlow(ctx context.Context, flowID int64) (model.ResultType, error) {
	uid, err := validatePermissionWithFlowID(ctx, "flows.delete", flowID, r.DB)
	if err != nil {
		return model.ResultTypeError, err
	}

	r.Logger.WithFields(logrus.Fields{
		"uid":  uid,
		"flow": flowID,
	}).Debug("delete flow")

	// Goes through the controller rather than GetFlow + worker.Finish: only
	// FinishFlow evicts the worker from the in-memory map. A worker left behind
	// for a soft-deleted flow leaks for the process lifetime, still shows up in
	// ListFlows, and can be finished a second time by a concurrent caller.
	if err := r.Controller.FinishFlow(ctx, flowID); err != nil &&
		!errors.Is(err, controller.ErrFlowNotFound) {
		return model.ResultTypeError, err
	}

	flow, err := r.DB.GetFlow(ctx, flowID)
	if err != nil {
		return model.ResultTypeError, err
	}

	containers, err := r.DB.GetFlowContainers(ctx, flow.ID)
	if err != nil {
		return model.ResultTypeError, err
	}

	if _, err := r.DB.DeleteFlow(ctx, flow.ID); err != nil {
		return model.ResultTypeError, err
	}

	// Best-effort cleanup: remove accumulated long-term memory documents for this
	// flow from the vector store. They will never be re-used after the flow is gone.
	if err := r.DB.DeleteFlowMemoryDocuments(ctx,
		database.StringToNullString(fmt.Sprintf("%d", flow.ID)),
	); err != nil {
		r.Logger.WithError(err).Warnf("failed to clean up memory documents for deleted flow %d", flow.ID)
	}

	prefs, err := r.DB.DeleteFavoriteFlow(ctx, database.DeleteFavoriteFlowParams{
		FlowID: flow.ID,
		UserID: flow.UserID,
	})
	switch {
	case err != nil && !errors.Is(err, sql.ErrNoRows):
		r.Logger.WithError(err).Warnf("failed to drop deleted flow %d from favorites", flow.ID)
	case err == nil:
		r.Subscriptions.NewSettingsPublisher(flow.UserID).SettingsUserUpdated(ctx, prefs)
	}

	publisher := r.Subscriptions.NewFlowPublisher(flow.UserID, flow.ID)
	publisher.FlowUpdated(ctx, flow, containers)
	publisher.FlowDeleted(ctx, flow, containers)

	return model.ResultTypeSuccess, nil
}

// RenameFlow is the resolver for the renameFlow field.
func (r *mutationResolver) RenameFlow(ctx context.Context, flowID int64, title string) (model.ResultType, error) {
	uid, err := validatePermissionWithFlowID(ctx, "flows.edit", flowID, r.DB)
	if err != nil {
		return model.ResultTypeError, err
	}

	if title == "" {
		return model.ResultTypeError, fmt.Errorf("flow title is required")
	}

	r.Logger.WithFields(logrus.Fields{
		"uid":   uid,
		"flow":  flowID,
		"title": title,
	}).Debug("rename flow")

	err = r.Controller.RenameFlow(ctx, flowID, title)
	if errors.Is(err, controller.ErrFlowNotFound) || errors.Is(err, controller.ErrFlowNotLoaded) {
		flow, err := r.DB.UpdateFlowTitle(ctx, database.UpdateFlowTitleParams{
			ID:    flowID,
			Title: title,
		})
		if err != nil {
			return model.ResultTypeError, err
		}

		containers, err := r.DB.GetFlowContainers(ctx, flow.ID)
		if err != nil {
			return model.ResultTypeError, err
		}

		publisher := r.Subscriptions.NewFlowPublisher(flow.UserID, flow.ID)
		publisher.FlowUpdated(ctx, flow, containers)
	} else if err != nil {
		return model.ResultTypeError, err
	}

	return model.ResultTypeSuccess, nil
}

// CreateAssistant is the resolver for the createAssistant field.
func (r *mutationResolver) CreateAssistant(ctx context.Context, flowID int64, modelProvider string, input string, useAgents bool, resourceIds []int64) (*model.FlowAssistant, error) {
	var (
		err error
		uid int64
	)

	if flowID == 0 {
		if _, _, err = validatePermission(ctx, "assistants.create"); err != nil {
			return nil, err
		}
		uid, _, err = validatePermission(ctx, "flows.create")
		if err != nil {
			return nil, err
		}
	} else {
		uid, err = validatePermissionWithFlowID(ctx, "assistants.create", flowID, r.DB)
		if err != nil {
			return nil, err
		}
	}

	r.Logger.WithFields(logrus.Fields{
		"uid":      uid,
		"flow":     flowID,
		"provider": modelProvider,
		"input":    input,
	}).Debug("create assistant")

	if modelProvider == "" {
		return nil, fmt.Errorf("model provider is required")
	}

	if input == "" {
		return nil, fmt.Errorf("user input is required")
	}

	var dbResources []database.UserResource
	if _, isAdmin, err := validatePermission(ctx, "resources.view"); err == nil {
		dbResources, err = validateUserResources(ctx, r.DB, uid, isAdmin, resourceIds)
		if err != nil {
			return nil, err
		}
	}

	prvname := provider.ProviderName(modelProvider)
	prv, err := r.ProvidersCtrl.GetProvider(ctx, prvname, uid)
	if err != nil {
		return nil, err
	}
	prvtype := prv.Type()

	assistantID, err := r.Controller.CreateAssistant(
		ctx, uid, flowID, input, useAgents, prvname, prvtype, nil, dbResources,
	)
	if err != nil {
		return nil, err
	}

	assistant, err := r.DB.GetAssistant(ctx, assistantID)
	if err != nil {
		return nil, err
	}

	flow, err := r.DB.GetFlow(ctx, assistant.FlowID)
	if err != nil {
		return nil, err
	}

	containers, err := r.DB.GetFlowContainers(ctx, assistant.FlowID)
	if err != nil {
		return nil, err
	}

	return converter.ConvertFlowAssistant(flow, containers, assistant), nil
}

// CallAssistant is the resolver for the callAssistant field.
func (r *mutationResolver) CallAssistant(ctx context.Context, flowID int64, assistantID int64, input string, useAgents bool, resourceIds []int64) (model.ResultType, error) {
	uid, err := validatePermissionWithFlowID(ctx, "assistants.edit", flowID, r.DB)
	if err != nil {
		return model.ResultTypeError, err
	}

	if input == "" {
		return model.ResultTypeError, fmt.Errorf("user input is required")
	}

	r.Logger.WithFields(logrus.Fields{
		"uid":       uid,
		"flow":      flowID,
		"assistant": assistantID,
	}).Debug("call assistant")

	var dbResources []database.UserResource
	if _, isAdmin, err := validatePermission(ctx, "resources.view"); err == nil {
		dbResources, err = validateUserResources(ctx, r.DB, uid, isAdmin, resourceIds)
		if err != nil {
			return model.ResultTypeError, err
		}
	}

	fw, err := r.Controller.GetFlow(ctx, flowID)
	if err != nil {
		return model.ResultTypeError, err
	}

	aw, err := fw.GetAssistant(ctx, assistantID)
	if err != nil {
		return model.ResultTypeError, err
	}

	if err := aw.PutInput(ctx, input, useAgents, dbResources); err != nil {
		return model.ResultTypeError, err
	}

	return model.ResultTypeSuccess, nil
}

// StopAssistant is the resolver for the stopAssistant field.
func (r *mutationResolver) StopAssistant(ctx context.Context, flowID int64, assistantID int64) (*model.Assistant, error) {
	uid, err := validatePermissionWithFlowID(ctx, "assistants.edit", flowID, r.DB)
	if err != nil {
		return nil, err
	}

	r.Logger.WithFields(logrus.Fields{
		"uid":       uid,
		"flow":      flowID,
		"assistant": assistantID,
	}).Debug("stop assistant")

	fw, err := r.Controller.GetFlow(ctx, flowID)
	if err != nil {
		return nil, err
	}

	aw, err := fw.GetAssistant(ctx, assistantID)
	if err != nil {
		return nil, err
	}

	if err := aw.Stop(ctx); err != nil {
		return nil, err
	}

	assistant, err := r.DB.GetFlowAssistant(ctx, database.GetFlowAssistantParams{
		ID:     assistantID,
		FlowID: flowID,
	})
	if err != nil {
		return nil, err
	}

	r.Subscriptions.NewFlowPublisher(fw.GetUserID(), flowID).AssistantUpdated(ctx, assistant)

	return converter.ConvertAssistant(assistant), nil
}

// DeleteAssistant is the resolver for the deleteAssistant field.
func (r *mutationResolver) DeleteAssistant(ctx context.Context, flowID int64, assistantID int64) (model.ResultType, error) {
	uid, err := validatePermissionWithFlowID(ctx, "assistants.delete", flowID, r.DB)
	if err != nil {
		return model.ResultTypeError, err
	}

	r.Logger.WithFields(logrus.Fields{
		"uid":       uid,
		"flow":      flowID,
		"assistant": assistantID,
	}).Debug("delete assistant")

	fw, err := r.Controller.GetFlow(ctx, flowID)
	if err != nil {
		return model.ResultTypeError, err
	}

	assistant, err := r.DB.GetFlowAssistant(ctx, database.GetFlowAssistantParams{
		ID:     assistantID,
		FlowID: flowID,
	})
	if err != nil {
		return model.ResultTypeError, err
	}

	if err := fw.DeleteAssistant(ctx, assistantID); err != nil {
		return model.ResultTypeError, err
	}

	r.Subscriptions.NewFlowPublisher(fw.GetUserID(), flowID).AssistantDeleted(ctx, assistant)

	return model.ResultTypeSuccess, nil
}

// TestAgent is the resolver for the testAgent field.
func (r *mutationResolver) TestAgent(ctx context.Context, typeArg model.ProviderType, agentType model.AgentConfigType, agent model.AgentConfig, simple *model.AgentConfig) (*model.AgentTestResult, error) {
	uid, _, err := validatePermission(ctx, "settings.providers.view")
	if err != nil {
		return nil, err
	}

	r.Logger.WithFields(logrus.Fields{
		"uid":  uid,
		"type": typeArg.String(),
	}).Debug("test agent")

	cfg := converter.ConvertAgentConfigFromGqlModel(&agent)
	prvtype := provider.ProviderType(typeArg)
	atype := pconfig.ProviderOptionsType(agentType)
	result, err := r.ProvidersCtrl.TestAgent(ctx, prvtype, atype, cfg, converter.ConvertAgentConfigFromGqlModel(simple))
	if err != nil {
		return nil, err
	}

	return converter.ConvertTestResults(result), nil
}

// TestProvider is the resolver for the testProvider field.
func (r *mutationResolver) TestProvider(ctx context.Context, typeArg model.ProviderType, agents model.AgentsConfig) (*model.ProviderTestResult, error) {
	uid, _, err := validatePermission(ctx, "settings.providers.view")
	if err != nil {
		return nil, err
	}

	r.Logger.WithFields(logrus.Fields{
		"uid":  uid,
		"type": typeArg.String(),
	}).Debug("test provider")

	cfg := converter.ConvertAgentsConfigFromGqlModel(&agents)
	prvtype := provider.ProviderType(typeArg)
	result, err := r.ProvidersCtrl.TestProvider(ctx, prvtype, cfg)
	if err != nil {
		return nil, err
	}

	return converter.ConvertProviderTestResults(result), nil
}

// CreateProvider is the resolver for the createProvider field.
func (r *mutationResolver) CreateProvider(ctx context.Context, name string, typeArg model.ProviderType, agents model.AgentsConfig) (*model.ProviderConfig, error) {
	uid, _, err := validatePermission(ctx, "settings.providers.edit")
	if err != nil {
		return nil, err
	}

	r.Logger.WithFields(logrus.Fields{
		"uid":  uid,
		"name": name,
		"type": typeArg.String(),
	}).Debug("create provider")

	cfg := converter.ConvertAgentsConfigFromGqlModel(&agents)
	prvname, prvtype := provider.ProviderName(name), provider.ProviderType(typeArg)
	prv, err := r.ProvidersCtrl.CreateProvider(ctx, uid, prvname, prvtype, cfg)
	if err != nil {
		return nil, err
	}

	r.Subscriptions.NewProviderPublisher(uid).ProviderCreated(ctx, prv, cfg)

	return converter.ConvertProvider(prv, cfg), nil
}

// UpdateProvider is the resolver for the updateProvider field.
func (r *mutationResolver) UpdateProvider(ctx context.Context, providerID int64, name string, agents model.AgentsConfig) (*model.ProviderConfig, error) {
	uid, _, err := validatePermission(ctx, "settings.providers.edit")
	if err != nil {
		return nil, err
	}

	r.Logger.WithFields(logrus.Fields{
		"uid":      uid,
		"provider": providerID,
		"name":     name,
	}).Debug("update provider")

	// Fetch the current name before the rename so the flow/assistant cascade
	// below knows which old name to look for; UpdateProvider itself only
	// returns the row *after* the rename.
	existing, err := r.DB.GetUserProvider(ctx, database.GetUserProviderParams{ID: providerID, UserID: uid})
	if err != nil {
		return nil, fmt.Errorf("failed to get provider %d: %w", providerID, err)
	}
	oldName := provider.ProviderName(existing.Name)

	cfg := converter.ConvertAgentsConfigFromGqlModel(&agents)
	prvname := provider.ProviderName(name)
	prv, err := r.ProvidersCtrl.UpdateProvider(ctx, uid, providerID, prvname, cfg)
	if err != nil {
		return nil, err
	}

	r.Subscriptions.NewProviderPublisher(uid).ProviderUpdated(ctx, prv, cfg)

	newName := provider.ProviderName(prv.Name)
	if oldName != newName {
		if err := r.Controller.RenameFlowsProvider(ctx, uid, oldName, newName); err != nil {
			r.Logger.WithError(err).Error("failed to cascade provider rename to flows/assistants")
		}
	}

	return converter.ConvertProvider(prv, cfg), nil
}

// DeleteProvider is the resolver for the deleteProvider field.
func (r *mutationResolver) DeleteProvider(ctx context.Context, providerID int64) (model.ResultType, error) {
	uid, _, err := validatePermission(ctx, "settings.providers.edit")
	if err != nil {
		return model.ResultTypeError, err
	}

	r.Logger.WithFields(logrus.Fields{
		"uid":      uid,
		"provider": providerID,
	}).Debug("delete provider")

	prv, err := r.ProvidersCtrl.DeleteProvider(ctx, uid, providerID)
	if err != nil {
		return model.ResultTypeError, err
	}

	// Runs before anything else that can fail: the provider row is already
	// soft-deleted, so bailing out earlier would leave the references dangling.
	// Rows whose stored name still resolves (an override of a built-in) are left
	// alone — running flows drop the deleted configuration on their next input or
	// on the next start. Never fails the mutation: the provider is gone either way.
	deletedName := provider.ProviderName(prv.Name)
	deletedType := provider.ProviderType(prv.Type)
	if err := r.Controller.ResetFlowsProviderToDefault(ctx, uid, deletedName, deletedType); err != nil {
		r.Logger.WithError(err).Error("failed to cascade provider deletion to flows/assistants")
	}

	var cfg pconfig.ProviderConfig
	if err := json.Unmarshal(prv.Config, &cfg); err != nil {
		return model.ResultTypeError, err
	}

	r.Subscriptions.NewProviderPublisher(uid).ProviderDeleted(ctx, prv, &cfg)

	return model.ResultTypeSuccess, nil
}

// ValidatePrompt is the resolver for the validatePrompt field.
func (r *mutationResolver) ValidatePrompt(ctx context.Context, typeArg model.PromptType, template string) (*model.PromptValidationResult, error) {
	uid, _, err := validatePermission(ctx, "settings.prompts.edit")
	if err != nil {
		return nil, err
	}

	r.Logger.WithFields(logrus.Fields{
		"uid":      uid,
		"type":     typeArg.String(),
		"template": template[:min(len(template), 1000)],
	}).Debug("validate prompt")

	var (
		result    = model.ResultTypeSuccess
		errorType *model.PromptValidationErrorType
		message   *string
		line      *int
		details   *string
	)

	if err := validator.ValidatePrompt(templates.PromptType(typeArg), template); err != nil {
		result = model.ResultTypeError
		errType := model.PromptValidationErrorTypeUnknownType
		if err, ok := err.(*validator.ValidationError); ok {
			switch err.Type {
			case validator.ErrorTypeSyntax:
				errType = model.PromptValidationErrorTypeSyntaxError
			case validator.ErrorTypeUnauthorizedVar:
				errType = model.PromptValidationErrorTypeUnauthorizedVariable
			case validator.ErrorTypeRenderingFailed:
				errType = model.PromptValidationErrorTypeRenderingFailed
			case validator.ErrorTypeEmptyTemplate:
				errType = model.PromptValidationErrorTypeEmptyTemplate
			case validator.ErrorTypeVariableTypeMismatch:
				errType = model.PromptValidationErrorTypeVariableTypeMismatch
			}
			if err.Message != "" {
				message = &err.Message
			}
			if err.Line > 0 {
				line = &err.Line
			}
			if err.Details != "" {
				details = &err.Details
			}
		}
		errorType = &errType
	}

	return &model.PromptValidationResult{
		Result:    result,
		ErrorType: errorType,
		Message:   message,
		Line:      line,
		Details:   details,
	}, nil
}

// CreatePrompt is the resolver for the createPrompt field.
func (r *mutationResolver) CreatePrompt(ctx context.Context, typeArg model.PromptType, template string) (*model.UserPrompt, error) {
	uid, _, err := validatePermission(ctx, "settings.prompts.edit")
	if err != nil {
		return nil, err
	}

	r.Logger.WithFields(logrus.Fields{
		"uid":      uid,
		"type":     typeArg.String(),
		"template": template[:min(len(template), 1000)],
	}).Debug("create prompt")

	if err := validator.ValidatePrompt(templates.PromptType(typeArg), template); err != nil {
		return nil, err
	}

	prompt, err := r.DB.CreateUserPrompt(ctx, database.CreateUserPromptParams{
		UserID: uid,
		Type:   database.PromptType(typeArg),
		Prompt: template,
	})
	if err != nil {
		return nil, err
	}

	return converter.ConvertPrompt(prompt), nil
}

// UpdatePrompt is the resolver for the updatePrompt field.
func (r *mutationResolver) UpdatePrompt(ctx context.Context, promptID int64, template string) (*model.UserPrompt, error) {
	uid, _, err := validatePermission(ctx, "settings.prompts.edit")
	if err != nil {
		return nil, err
	}

	r.Logger.WithFields(logrus.Fields{
		"uid":      uid,
		"prompt":   promptID,
		"template": template[:min(len(template), 1000)],
	}).Debug("update prompt")

	prompt, err := r.DB.GetUserPrompt(ctx, database.GetUserPromptParams{
		ID:     promptID,
		UserID: uid,
	})
	if err != nil {
		return nil, err
	}

	if err := validator.ValidatePrompt(templates.PromptType(prompt.Type), template); err != nil {
		return nil, err
	}

	prompt, err = r.DB.UpdateUserPrompt(ctx, database.UpdateUserPromptParams{
		ID:     promptID,
		Prompt: template,
		UserID: uid,
	})
	if err != nil {
		return nil, err
	}

	return converter.ConvertPrompt(prompt), nil
}

// DeletePrompt is the resolver for the deletePrompt field.
func (r *mutationResolver) DeletePrompt(ctx context.Context, promptID int64) (model.ResultType, error) {
	uid, _, err := validatePermission(ctx, "settings.prompts.edit")
	if err != nil {
		return model.ResultTypeError, err
	}

	r.Logger.WithFields(logrus.Fields{
		"uid":    uid,
		"prompt": promptID,
	}).Debug("delete prompt")

	if _, err = r.DB.GetUserPrompt(ctx, database.GetUserPromptParams{
		ID:     promptID,
		UserID: uid,
	}); err != nil {
		return model.ResultTypeError, fmt.Errorf("prompt not found: %w", err)
	}

	err = r.DB.DeleteUserPrompt(ctx, database.DeleteUserPromptParams{
		ID:     promptID,
		UserID: uid,
	})
	if err != nil {
		return model.ResultTypeError, err
	}

	return model.ResultTypeSuccess, nil
}

// CreateAPIToken is the resolver for the createAPIToken field.
func (r *mutationResolver) CreateAPIToken(ctx context.Context, input model.CreateAPITokenInput) (*model.APITokenWithSecret, error) {
	uid, _, err := validatePermission(ctx, "settings.tokens.create")
	if err != nil {
		return nil, err
	}

	isUserSession, err := validateUserType(ctx, userSessionTypes...)
	if err != nil {
		return nil, err
	}

	if !isUserSession {
		return nil, fmt.Errorf("unauthorized: non-user session is not allowed to create API tokens")
	}

	if r.Config.CookieSigningSalt == "" || r.Config.CookieSigningSalt == "salt" {
		return nil, fmt.Errorf("token creation is disabled with default salt")
	}

	if input.TTL < 60 || input.TTL > 94608000 {
		return nil, fmt.Errorf("invalid TTL: must be between 60 and 94608000 seconds")
	}

	if input.Name != nil && utf8.RuneCountInString(*input.Name) > maxAPITokenNameLen {
		return nil, fmt.Errorf("token name must not exceed %d characters", maxAPITokenNameLen)
	}

	r.Logger.WithFields(logrus.Fields{
		"uid":  uid,
		"name": input.Name,
		"ttl":  input.TTL,
	}).Debug("create api token")

	user, err := r.DB.GetUser(ctx, uid)
	if err != nil {
		return nil, err
	}

	tokenID, err := auth.GenerateTokenID()
	if err != nil {
		return nil, fmt.Errorf("failed to generate token ID: %w", err)
	}

	claims := auth.MakeAPITokenClaims(tokenID, user.Hash, uint64(uid), uint64(user.RoleID), uint64(input.TTL))

	tokenString, err := auth.MakeAPIToken(r.Config.AuthSalt(), claims)
	if err != nil {
		return nil, fmt.Errorf("failed to create token: %w", err)
	}

	var nameStr sql.NullString
	if input.Name != nil && *input.Name != "" {
		nameStr = sql.NullString{String: *input.Name, Valid: true}
	}

	apiToken, err := r.DB.CreateAPIToken(ctx, database.CreateAPITokenParams{
		TokenID: tokenID,
		UserID:  uid,
		RoleID:  user.RoleID,
		Name:    nameStr,
		Ttl:     int64(input.TTL),
		Status:  database.TokenStatusActive,
	})
	if err != nil {
		return nil, fmt.Errorf("failed to create token in database: %w", err)
	}

	tokenWithSecret := database.APITokenWithSecret{
		ApiToken: apiToken,
		Token:    tokenString,
	}

	r.TokenCache.Invalidate(tokenID)
	r.TokenCache.InvalidateUser(uint64(uid))

	r.Subscriptions.NewAPITokenPublisher(uid).APITokenCreated(ctx, tokenWithSecret)

	return converter.ConvertAPITokenWithSecret(tokenWithSecret), nil
}

// UpdateAPIToken is the resolver for the updateAPIToken field.
func (r *mutationResolver) UpdateAPIToken(ctx context.Context, tokenID string, input model.UpdateAPITokenInput) (*model.APIToken, error) {
	uid, _, err := validatePermission(ctx, "settings.tokens.edit")
	if err != nil {
		return nil, err
	}

	isUserSession, err := validateUserType(ctx, userSessionTypes...)
	if err != nil {
		return nil, err
	}

	if !isUserSession {
		return nil, fmt.Errorf("unauthorized: non-user session is not allowed to update API tokens")
	}

	if input.Name != nil && utf8.RuneCountInString(*input.Name) > maxAPITokenNameLen {
		return nil, fmt.Errorf("token name must not exceed %d characters", maxAPITokenNameLen)
	}

	r.Logger.WithFields(logrus.Fields{
		"uid":     uid,
		"tokenID": tokenID,
	}).Debug("update api token")

	token, err := r.DB.GetUserAPITokenByTokenID(ctx, database.GetUserAPITokenByTokenIDParams{
		TokenID: tokenID,
		UserID:  uid,
	})
	if err != nil {
		return nil, fmt.Errorf("token not found: %w", err)
	}

	var nameStr sql.NullString
	if input.Name != nil {
		if *input.Name != "" {
			nameStr = sql.NullString{String: *input.Name, Valid: true}
		}
	} else {
		nameStr = token.Name
	}

	status := token.Status
	if input.Status != nil {
		switch s := *input.Status; s {
		case model.TokenStatusActive:
			status = database.TokenStatusActive
		case model.TokenStatusRevoked:
			status = database.TokenStatusRevoked
		case model.TokenStatusExpired:
			// Derived from the ttl, never stored: keep whatever the row holds.
		default:
			return nil, fmt.Errorf("invalid token status: %s", s.String())
		}
	}

	updatedToken, err := r.DB.UpdateUserAPIToken(ctx, database.UpdateUserAPITokenParams{
		ID:     token.ID,
		UserID: uid,
		Name:   nameStr,
		Status: status,
	})
	if err != nil {
		return nil, fmt.Errorf("failed to update token: %w", err)
	}

	if status != token.Status {
		r.TokenCache.Invalidate(tokenID)
		r.TokenCache.InvalidateUser(uint64(uid))
	}

	r.Subscriptions.NewAPITokenPublisher(uid).APITokenUpdated(ctx, updatedToken)

	return converter.ConvertAPIToken(updatedToken), nil
}

// DeleteAPIToken is the resolver for the deleteAPIToken field.
func (r *mutationResolver) DeleteAPIToken(ctx context.Context, tokenID string) (bool, error) {
	uid, _, err := validatePermission(ctx, "settings.tokens.delete")
	if err != nil {
		return false, err
	}

	isUserSession, err := validateUserType(ctx, userSessionTypes...)
	if err != nil {
		return false, err
	}

	if !isUserSession {
		return false, fmt.Errorf("unauthorized: non-user session is not allowed to delete API tokens")
	}

	r.Logger.WithFields(logrus.Fields{
		"uid":     uid,
		"tokenID": tokenID,
	}).Debug("delete api token")

	token, err := r.DB.DeleteUserAPITokenByTokenID(ctx, database.DeleteUserAPITokenByTokenIDParams{
		TokenID: tokenID,
		UserID:  uid,
	})
	if err != nil {
		return false, fmt.Errorf("failed to delete token: %w", err)
	}

	r.TokenCache.Invalidate(tokenID)
	r.TokenCache.InvalidateUser(uint64(uid))

	r.Subscriptions.NewAPITokenPublisher(uid).APITokenDeleted(ctx, token)

	return true, nil
}

// AddFavoriteFlow is the resolver for the addFavoriteFlow field.
func (r *mutationResolver) AddFavoriteFlow(ctx context.Context, flowID int64) (model.ResultType, error) {
	_, err := validatePermissionWithFlowID(ctx, "flows.view", flowID, r.DB)
	if err != nil {
		return model.ResultTypeError, err
	}

	uid, _, err := validatePermission(ctx, "settings.user.edit")
	if err != nil {
		return model.ResultTypeError, err
	}

	isUserSession, err := validateUserType(ctx, userSessionTypes...)
	if err != nil {
		return model.ResultTypeError, err
	}

	if !isUserSession {
		return model.ResultTypeError, fmt.Errorf("unauthorized: non-user session is not allowed to manage favorites")
	}

	r.Logger.WithFields(logrus.Fields{
		"uid":    uid,
		"flowID": flowID,
	}).Debug("add favorite flow")

	flow, err := r.DB.GetFlow(ctx, flowID)
	if err != nil {
		return model.ResultTypeError, fmt.Errorf("flow not found: %w", err)
	}

	if flow.UserID != uid {
		return model.ResultTypeError, fmt.Errorf("%w: flow belongs to another user", ErrForbidden)
	}

	prefs, err := r.DB.AddFavoriteFlow(ctx, database.AddFavoriteFlowParams{
		UserID: uid,
		FlowID: flowID,
	})
	if err != nil {
		return model.ResultTypeError, fmt.Errorf("failed to add favorite flow: %w", err)
	}

	r.Subscriptions.NewSettingsPublisher(uid).SettingsUserUpdated(ctx, prefs)

	return model.ResultTypeSuccess, nil
}

// DeleteFavoriteFlow is the resolver for the deleteFavoriteFlow field.
func (r *mutationResolver) DeleteFavoriteFlow(ctx context.Context, flowID int64) (model.ResultType, error) {
	_, err := validatePermissionWithFlowID(ctx, "flows.view", flowID, r.DB)
	if err != nil {
		return model.ResultTypeError, err
	}

	uid, err := validatePermissionWithFlowID(ctx, "settings.user.edit", flowID, r.DB)
	if err != nil {
		return model.ResultTypeError, err
	}

	isUserSession, err := validateUserType(ctx, userSessionTypes...)
	if err != nil {
		return model.ResultTypeError, err
	}

	if !isUserSession {
		return model.ResultTypeError, fmt.Errorf("unauthorized: non-user session is not allowed to manage favorites")
	}

	r.Logger.WithFields(logrus.Fields{
		"uid":    uid,
		"flowID": flowID,
	}).Debug("delete favorite flow")

	prefs, err := r.DB.DeleteFavoriteFlow(ctx, database.DeleteFavoriteFlowParams{
		FlowID: flowID,
		UserID: uid,
	})
	if err != nil {
		return model.ResultTypeError, fmt.Errorf("failed to delete favorite flow: %w", err)
	}

	r.Subscriptions.NewSettingsPublisher(uid).SettingsUserUpdated(ctx, prefs)

	return model.ResultTypeSuccess, nil
}

// CreateFlowTemplate is the resolver for the createFlowTemplate field.
func (r *mutationResolver) CreateFlowTemplate(ctx context.Context, input model.CreateFlowTemplateInput) (*model.FlowTemplate, error) {
	uid, _, err := validatePermission(ctx, "templates.create")
	if err != nil {
		return nil, err
	}

	isUserSession, err := validateUserType(ctx, userSessionTypes...)
	if err != nil {
		return nil, err
	}

	if !isUserSession {
		return nil, fmt.Errorf("unauthorized: non-user session is not allowed to create templates")
	}

	title, text, err := validateFlowTemplateFields(input.Title, input.Text)
	if err != nil {
		return nil, err
	}

	r.Logger.WithFields(logrus.Fields{
		"uid":   uid,
		"title": input.Title,
	}).Debug("create flow template")

	template, err := r.DB.CreateFlowTemplate(ctx, database.CreateFlowTemplateParams{
		UserID: uid,
		Title:  title,
		Text:   text,
	})
	if err != nil {
		return nil, fmt.Errorf("failed to create template: %w", err)
	}

	r.Subscriptions.NewFlowTemplatePublisher(uid).FlowTemplateCreated(ctx, template)

	return converter.ConvertFlowTemplate(template), nil
}

// UpdateFlowTemplate is the resolver for the updateFlowTemplate field.
func (r *mutationResolver) UpdateFlowTemplate(ctx context.Context, templateID int64, input model.UpdateFlowTemplateInput) (*model.FlowTemplate, error) {
	uid, _, err := validatePermission(ctx, "templates.edit")
	if err != nil {
		return nil, err
	}

	isUserSession, err := validateUserType(ctx, userSessionTypes...)
	if err != nil {
		return nil, err
	}

	if !isUserSession {
		return nil, fmt.Errorf("unauthorized: non-user session is not allowed to update templates")
	}

	title, text, err := validateFlowTemplateFields(input.Title, input.Text)
	if err != nil {
		return nil, err
	}

	r.Logger.WithFields(logrus.Fields{
		"uid":        uid,
		"templateID": templateID,
	}).Debug("update flow template")

	_, err = r.DB.GetFlowTemplate(ctx, database.GetFlowTemplateParams{
		ID:     templateID,
		UserID: uid,
	})
	if err != nil {
		return nil, fmt.Errorf("template not found: %w", err)
	}

	template, err := r.DB.UpdateFlowTemplate(ctx, database.UpdateFlowTemplateParams{
		ID:     templateID,
		UserID: uid,
		Title:  title,
		Text:   text,
	})
	if err != nil {
		return nil, fmt.Errorf("failed to update template: %w", err)
	}

	r.Subscriptions.NewFlowTemplatePublisher(uid).FlowTemplateUpdated(ctx, template)

	return converter.ConvertFlowTemplate(template), nil
}

// DeleteFlowTemplate is the resolver for the deleteFlowTemplate field.
func (r *mutationResolver) DeleteFlowTemplate(ctx context.Context, templateID int64) (model.ResultType, error) {
	uid, _, err := validatePermission(ctx, "templates.delete")
	if err != nil {
		return model.ResultTypeError, err
	}

	isUserSession, err := validateUserType(ctx, userSessionTypes...)
	if err != nil {
		return model.ResultTypeError, err
	}

	if !isUserSession {
		return model.ResultTypeError, fmt.Errorf("unauthorized: non-user session is not allowed to delete templates")
	}

	r.Logger.WithFields(logrus.Fields{
		"uid":        uid,
		"templateID": templateID,
	}).Debug("delete flow template")

	template, err := r.DB.GetFlowTemplate(ctx, database.GetFlowTemplateParams{
		ID:     templateID,
		UserID: uid,
	})
	if err != nil {
		return model.ResultTypeError, fmt.Errorf("template not found: %w", err)
	}

	err = r.DB.DeleteFlowTemplate(ctx, database.DeleteFlowTemplateParams{
		ID:     templateID,
		UserID: uid,
	})
	if err != nil {
		return model.ResultTypeError, fmt.Errorf("failed to delete template: %w", err)
	}

	r.Subscriptions.NewFlowTemplatePublisher(uid).FlowTemplateDeleted(ctx, template)

	return model.ResultTypeSuccess, nil
}

// CreateKnowledgeDocument is the resolver for the createKnowledgeDocument field.
func (r *mutationResolver) CreateKnowledgeDocument(ctx context.Context, input model.CreateKnowledgeDocumentInput) (*model.KnowledgeDocument, error) {
	uid, _, err := validatePermission(ctx, "knowledge.create")
	if err != nil {
		return nil, err
	}

	if strings.TrimSpace(input.Content) == "" {
		return nil, fmt.Errorf("content is required")
	}
	if err := validateKnowledgeQuestion(&input.Question); err != nil {
		return nil, err
	}
	if err := validateKnowledgeFieldLengths(input.Content, &input.Question, input.Description, input.CodeLang); err != nil {
		return nil, err
	}

	r.Logger.WithFields(logrus.Fields{
		"uid":      uid,
		"doc_type": input.DocType,
	}).Debug("create knowledge document")

	return r.Knowledge.CreateDocument(ctx, uid, input)
}

// UpdateKnowledgeDocument is the resolver for the updateKnowledgeDocument field.
func (r *mutationResolver) UpdateKnowledgeDocument(ctx context.Context, id string, input model.UpdateKnowledgeDocumentInput) (*model.KnowledgeDocument, error) {
	uid, admin, err := validatePermission(ctx, "knowledge.edit")
	if err != nil {
		return nil, err
	}

	if strings.TrimSpace(input.Content) == "" {
		return nil, fmt.Errorf("content is required")
	}
	if err := validateKnowledgeQuestion(input.Question); err != nil {
		return nil, err
	}
	if err := validateKnowledgeFieldLengths(input.Content, input.Question, input.Description, input.CodeLang); err != nil {
		return nil, err
	}

	r.Logger.WithFields(logrus.Fields{
		"uid":   uid,
		"admin": admin,
		"id":    id,
	}).Debug("update knowledge document")

	if admin {
		return r.Knowledge.UpdateDocument(ctx, uid, id, input)
	}
	return r.Knowledge.UpdateUserDocument(ctx, uid, id, input)
}

// RenameKnowledgeDocument is the resolver for the renameKnowledgeDocument field.
func (r *mutationResolver) RenameKnowledgeDocument(ctx context.Context, id string, question string) (*model.KnowledgeDocument, error) {
	uid, admin, err := validatePermission(ctx, "knowledge.edit")
	if err != nil {
		return nil, err
	}

	if err := validateKnowledgeQuestion(&question); err != nil {
		return nil, err
	}

	r.Logger.WithFields(logrus.Fields{
		"uid":   uid,
		"admin": admin,
		"id":    id,
	}).Debug("rename knowledge document")

	if admin {
		return r.Knowledge.RenameDocument(ctx, uid, id, question)
	}
	return r.Knowledge.RenameUserDocument(ctx, uid, id, question)
}

// DeleteKnowledgeDocument is the resolver for the deleteKnowledgeDocument field.
func (r *mutationResolver) DeleteKnowledgeDocument(ctx context.Context, id string) (model.ResultType, error) {
	uid, admin, err := validatePermission(ctx, "knowledge.delete")
	if err != nil {
		return model.ResultTypeError, err
	}

	r.Logger.WithFields(logrus.Fields{
		"uid":   uid,
		"admin": admin,
		"id":    id,
	}).Debug("delete knowledge document")

	var deleteErr error
	if admin {
		deleteErr = r.Knowledge.DeleteDocument(ctx, uid, id)
	} else {
		deleteErr = r.Knowledge.DeleteUserDocument(ctx, uid, id)
	}
	if deleteErr != nil {
		return model.ResultTypeError, deleteErr
	}
	return model.ResultTypeSuccess, nil
}

// AnonymizeText is the resolver for the anonymizeText field.
func (r *mutationResolver) AnonymizeText(ctx context.Context, text string) (string, error) {
	uid, _, err := validatePermission(ctx, "anonymize.call")
	if err != nil {
		return "", err
	}

	r.Logger.WithFields(logrus.Fields{
		"uid": uid,
	}).Debug("anonymize text")

	if r.Replacer == nil {
		return "", fmt.Errorf("anonymizer is not available")
	}

	return r.Replacer.ReplaceString(text), nil
}

// Providers is the resolver for the providers field.
func (r *queryResolver) Providers(ctx context.Context) ([]*model.Provider, error) {
	uid, _, err := validatePermission(ctx, "providers.view")
	if err != nil {
		return nil, err
	}

	r.Logger.WithFields(logrus.Fields{
		"uid": uid,
	}).Debug("get providers")

	providers, err := r.ProvidersCtrl.GetProviders(ctx, uid)
	if err != nil {
		return nil, err
	}

	providersList := make([]*model.Provider, len(providers))
	for i, prvname := range providers.ListNames() {
		providersList[i] = &model.Provider{
			Name: string(prvname),
			Type: model.ProviderType(providers[prvname].Type()),
		}
	}

	return providersList, nil
}

// Assistants is the resolver for the assistants field.
func (r *queryResolver) Assistants(ctx context.Context, flowID int64) ([]*model.Assistant, error) {
	uid, err := validatePermissionWithFlowID(ctx, "assistants.view", flowID, r.DB)
	if err != nil {
		return nil, err
	}

	r.Logger.WithFields(logrus.Fields{
		"uid":  uid,
		"flow": flowID,
	}).Debug("get assistants")

	assistants, err := r.DB.GetFlowAssistants(ctx, flowID)
	if err != nil {
		return nil, err
	}

	return converter.ConvertAssistants(assistants), nil
}

// Flows is the resolver for the flows field.
func (r *queryResolver) Flows(ctx context.Context) ([]*model.Flow, error) {
	uid, admin, err := validatePermission(ctx, "flows.view")
	if err != nil {
		return nil, err
	}

	r.Logger.WithFields(logrus.Fields{
		"uid": uid,
	}).Debug("get flows")

	var (
		flows      []database.Flow
		containers []database.Container
	)

	if admin {
		flows, err = r.DB.GetFlows(ctx)
	} else {
		flows, err = r.DB.GetUserFlows(ctx, uid)
	}
	if err != nil {
		return nil, err
	}

	if _, admin, err = validatePermission(ctx, "containers.view"); err == nil {
		if admin {
			containers, err = r.DB.GetContainers(ctx)
		} else {
			containers, err = r.DB.GetUserContainers(ctx, uid)
		}
		if err != nil {
			return nil, err
		}
	}

	return converter.ConvertFlows(flows, containers), nil
}

// Flow is the resolver for the flow field.
func (r *queryResolver) Flow(ctx context.Context, flowID int64) (*model.Flow, error) {
	uid, err := validatePermissionWithFlowID(ctx, "flows.view", flowID, r.DB)
	if err != nil {
		return nil, err
	}

	r.Logger.WithFields(logrus.Fields{
		"uid":  uid,
		"flow": flowID,
	}).Debug("get flow")

	var (
		flow       database.Flow
		containers []database.Container
	)

	flow, err = r.DB.GetFlow(ctx, flowID)
	if err != nil {
		return nil, err
	}

	if _, _, err = validatePermission(ctx, "containers.view"); err == nil {
		containers, err = r.DB.GetFlowContainers(ctx, flowID)
		if err != nil {
			return nil, err
		}
	}

	return converter.ConvertFlow(flow, containers), nil
}

// Tasks is the resolver for the tasks field.
func (r *queryResolver) Tasks(ctx context.Context, flowID int64) ([]*model.Task, error) {
	uid, err := validatePermissionWithFlowID(ctx, "tasks.view", flowID, r.DB)
	if err != nil {
		return nil, err
	}

	r.Logger.WithFields(logrus.Fields{
		"uid":  uid,
		"flow": flowID,
	}).Debug("get tasks")

	tasks, err := r.DB.GetFlowTasks(ctx, flowID)
	if err != nil {
		return nil, err
	}

	var subtasks []database.Subtask
	if _, _, err = validatePermission(ctx, "subtasks.view"); err == nil {
		subtasks, err = r.DB.GetFlowSubtasks(ctx, flowID)
		if err != nil {
			return nil, err
		}
	}

	return converter.ConvertTasks(tasks, subtasks), nil
}

// FlowFiles is the resolver for the flowFiles field.
func (r *queryResolver) FlowFiles(ctx context.Context, flowID int64) ([]*model.FlowFile, error) {
	uid, err := validatePermissionWithFlowID(ctx, "flow_files.view", flowID, r.DB)
	if err != nil {
		return nil, err
	}

	r.Logger.WithFields(logrus.Fields{
		"uid":  uid,
		"flow": flowID,
	}).Debug("get flow files")

	files, err := flowfiles.List(r.Config.DataDir, uint64(flowID))
	if err != nil {
		return nil, err
	}

	return convertFlowFiles(files, flowID), nil
}

// Screenshots is the resolver for the screenshots field.
func (r *queryResolver) Screenshots(ctx context.Context, flowID int64) ([]*model.Screenshot, error) {
	uid, err := validatePermissionWithFlowID(ctx, "screenshots.view", flowID, r.DB)
	if err != nil {
		return nil, err
	}

	r.Logger.WithFields(logrus.Fields{
		"uid":  uid,
		"flow": flowID,
	}).Debug("get screenshots")

	screenshots, err := r.DB.GetFlowScreenshots(ctx, flowID)
	if err != nil {
		return nil, err
	}

	return converter.ConvertScreenshots(screenshots), nil
}

// TerminalLogs is the resolver for the terminalLogs field.
func (r *queryResolver) TerminalLogs(ctx context.Context, flowID int64) ([]*model.TerminalLog, error) {
	uid, err := validatePermissionWithFlowID(ctx, "termlogs.view", flowID, r.DB)
	if err != nil {
		return nil, err
	}

	r.Logger.WithFields(logrus.Fields{
		"uid":  uid,
		"flow": flowID,
	}).Debug("get term logs")

	logs, err := r.DB.GetFlowTermLogs(ctx, flowID)
	if err != nil {
		return nil, err
	}

	return converter.ConvertTerminalLogs(logs), nil
}

// MessageLogs is the resolver for the messageLogs field.
func (r *queryResolver) MessageLogs(ctx context.Context, flowID int64) ([]*model.MessageLog, error) {
	uid, err := validatePermissionWithFlowID(ctx, "msglogs.view", flowID, r.DB)
	if err != nil {
		return nil, err
	}

	r.Logger.WithFields(logrus.Fields{
		"uid":  uid,
		"flow": flowID,
	}).Debug("get msg logs")

	logs, err := r.DB.GetFlowMsgLogs(ctx, flowID)
	if err != nil {
		return nil, err
	}

	return converter.ConvertMessageLogs(logs), nil
}

// AgentLogs is the resolver for the agentLogs field.
func (r *queryResolver) AgentLogs(ctx context.Context, flowID int64) ([]*model.AgentLog, error) {
	uid, err := validatePermissionWithFlowID(ctx, "agentlogs.view", flowID, r.DB)
	if err != nil {
		return nil, err
	}

	r.Logger.WithFields(logrus.Fields{
		"uid":  uid,
		"flow": flowID,
	}).Debug("get agent logs")

	logs, err := r.DB.GetFlowAgentLogs(ctx, flowID)
	if err != nil {
		return nil, err
	}

	return converter.ConvertAgentLogs(logs), nil
}

// SearchLogs is the resolver for the searchLogs field.
func (r *queryResolver) SearchLogs(ctx context.Context, flowID int64) ([]*model.SearchLog, error) {
	uid, err := validatePermissionWithFlowID(ctx, "searchlogs.view", flowID, r.DB)
	if err != nil {
		return nil, err
	}

	r.Logger.WithFields(logrus.Fields{
		"uid":  uid,
		"flow": flowID,
	}).Debug("get search logs")

	logs, err := r.DB.GetFlowSearchLogs(ctx, flowID)
	if err != nil {
		return nil, err
	}

	return converter.ConvertSearchLogs(logs), nil
}

// VectorStoreLogs is the resolver for the vectorStoreLogs field.
func (r *queryResolver) VectorStoreLogs(ctx context.Context, flowID int64) ([]*model.VectorStoreLog, error) {
	uid, err := validatePermissionWithFlowID(ctx, "vecstorelogs.view", flowID, r.DB)
	if err != nil {
		return nil, err
	}

	r.Logger.WithFields(logrus.Fields{
		"uid":  uid,
		"flow": flowID,
	}).Debug("get vector store logs")

	logs, err := r.DB.GetFlowVectorStoreLogs(ctx, flowID)
	if err != nil {
		return nil, err
	}

	return converter.ConvertVectorStoreLogs(logs), nil
}

// ToolCallLogs is the resolver for the toolCallLogs field.
func (r *queryResolver) ToolCallLogs(ctx context.Context, flowID int64) ([]*model.ToolCallLog, error) {
	uid, err := validatePermissionWithFlowID(ctx, "toolcalls.view", flowID, r.DB)
	if err != nil {
		return nil, err
	}

	r.Logger.WithFields(logrus.Fields{
		"uid":  uid,
		"flow": flowID,
	}).Debug("get tool call logs")

	logs, err := r.DB.GetFlowToolcalls(ctx, flowID)
	if err != nil {
		return nil, err
	}

	return converter.ConvertToolCallLogs(logs), nil
}

// AssistantLogs is the resolver for the assistantLogs field.
func (r *queryResolver) AssistantLogs(ctx context.Context, flowID int64, assistantID int64) ([]*model.AssistantLog, error) {
	uid, err := validatePermissionWithFlowID(ctx, "assistantlogs.view", flowID, r.DB)
	if err != nil {
		return nil, err
	}

	r.Logger.WithFields(logrus.Fields{
		"uid":       uid,
		"flow":      flowID,
		"assistant": assistantID,
	}).Debug("get assistant logs")

	logs, err := r.DB.GetFlowAssistantLogs(ctx, database.GetFlowAssistantLogsParams{
		FlowID:      flowID,
		AssistantID: assistantID,
	})
	if err != nil {
		return nil, err
	}

	return converter.ConvertAssistantLogs(logs), nil
}

// UsageStatsTotal is the resolver for the usageStatsTotal field.
func (r *queryResolver) UsageStatsTotal(ctx context.Context) (*model.UsageStats, error) {
	uid, _, err := validatePermission(ctx, "usage.view")
	if err != nil {
		return nil, err
	}

	r.Logger.WithFields(logrus.Fields{
		"uid": uid,
	}).Debug("get total usage stats")

	stats, err := r.DB.GetUserTotalUsageStats(ctx, uid)
	if err != nil {
		return nil, err
	}

	return converter.ConvertUsageStats(stats), nil
}

// UsageStatsByPeriod is the resolver for the usageStatsByPeriod field.
func (r *queryResolver) UsageStatsByPeriod(ctx context.Context, period model.UsageStatsPeriod, timezone *string) ([]*model.DailyUsageStats, error) {
	uid, _, err := validatePermission(ctx, "usage.view")
	if err != nil {
		return nil, err
	}

	days, err := periodDays(period)
	if err != nil {
		return nil, err
	}

	tz, err := r.Timezones.ResolveFor(timezone)
	if err != nil {
		return nil, err
	}

	r.Logger.WithFields(logrus.Fields{
		"uid":      uid,
		"period":   period,
		"timezone": tz,
	}).Debug("get usage stats by period")

	stats, err := r.DB.GetUsageStatsByDay(ctx, database.GetUsageStatsByDayParams{
		UserID: uid,
		Tz:     tz,
		Days:   days,
	})
	if err != nil {
		return nil, err
	}

	return converter.ConvertDailyUsageStats(stats), nil
}

// UsageStatsByProvider is the resolver for the usageStatsByProvider field.
func (r *queryResolver) UsageStatsByProvider(ctx context.Context) ([]*model.ProviderUsageStats, error) {
	uid, _, err := validatePermission(ctx, "usage.view")
	if err != nil {
		return nil, err
	}

	r.Logger.WithFields(logrus.Fields{
		"uid": uid,
	}).Debug("get usage stats by provider")

	stats, err := r.DB.GetUsageStatsByProvider(ctx, uid)
	if err != nil {
		return nil, err
	}

	return converter.ConvertProviderUsageStats(stats), nil
}

// UsageStatsByModel is the resolver for the usageStatsByModel field.
func (r *queryResolver) UsageStatsByModel(ctx context.Context) ([]*model.ModelUsageStats, error) {
	uid, _, err := validatePermission(ctx, "usage.view")
	if err != nil {
		return nil, err
	}

	r.Logger.WithFields(logrus.Fields{
		"uid": uid,
	}).Debug("get usage stats by model")

	stats, err := r.DB.GetUsageStatsByModel(ctx, uid)
	if err != nil {
		return nil, err
	}

	return converter.ConvertModelUsageStats(stats), nil
}

// UsageStatsByAgentType is the resolver for the usageStatsByAgentType field.
func (r *queryResolver) UsageStatsByAgentType(ctx context.Context) ([]*model.AgentTypeUsageStats, error) {
	uid, _, err := validatePermission(ctx, "usage.view")
	if err != nil {
		return nil, err
	}

	r.Logger.WithFields(logrus.Fields{
		"uid": uid,
	}).Debug("get usage stats by agent type")

	stats, err := r.DB.GetUsageStatsByType(ctx, uid)
	if err != nil {
		return nil, err
	}

	return converter.ConvertAgentTypeUsageStats(stats), nil
}

// UsageStatsByFlow is the resolver for the usageStatsByFlow field.
func (r *queryResolver) UsageStatsByFlow(ctx context.Context, flowID int64) (*model.UsageStats, error) {
	uid, err := validatePermissionWithFlowID(ctx, "usage.view", flowID, r.DB)
	if err != nil {
		return nil, err
	}

	r.Logger.WithFields(logrus.Fields{
		"uid":  uid,
		"flow": flowID,
	}).Debug("get usage stats by flow")

	stats, err := r.DB.GetFlowUsageStats(ctx, flowID)
	if err != nil {
		return nil, err
	}

	return converter.ConvertUsageStats(stats), nil
}

// UsageStatsByAgentTypeForFlow is the resolver for the usageStatsByAgentTypeForFlow field.
func (r *queryResolver) UsageStatsByAgentTypeForFlow(ctx context.Context, flowID int64) ([]*model.AgentTypeUsageStats, error) {
	uid, err := validatePermissionWithFlowID(ctx, "usage.view", flowID, r.DB)
	if err != nil {
		return nil, err
	}

	r.Logger.WithFields(logrus.Fields{
		"uid":  uid,
		"flow": flowID,
	}).Debug("get usage stats by agent type for flow")

	stats, err := r.DB.GetUsageStatsByTypeForFlow(ctx, flowID)
	if err != nil {
		return nil, err
	}

	return converter.ConvertAgentTypeUsageStatsForFlow(stats), nil
}

// UsageStatsByModelAgentsForFlow is the resolver for the usageStatsByModelAgentsForFlow field.
func (r *queryResolver) UsageStatsByModelAgentsForFlow(ctx context.Context, flowID int64) ([]*model.ModelAgentsUsageStats, error) {
	uid, err := validatePermissionWithFlowID(ctx, "usage.view", flowID, r.DB)
	if err != nil {
		return nil, err
	}

	r.Logger.WithFields(logrus.Fields{
		"uid":  uid,
		"flow": flowID,
	}).Debug("get usage stats by model agents for flow")

	stats, err := r.DB.GetUsageStatsByModelAgentsForFlow(ctx, flowID)
	if err != nil {
		return nil, err
	}

	return converter.ConvertModelAgentsUsageStatsForFlow(stats), nil
}

// ToolcallsStatsTotal is the resolver for the toolcallsStatsTotal field.
func (r *queryResolver) ToolcallsStatsTotal(ctx context.Context) (*model.ToolcallsStats, error) {
	uid, _, err := validatePermission(ctx, "usage.view")
	if err != nil {
		return nil, err
	}

	r.Logger.WithFields(logrus.Fields{
		"uid": uid,
	}).Debug("get total toolcalls stats")

	stats, err := r.DB.GetUserTotalToolcallsStats(ctx, uid)
	if err != nil {
		return nil, err
	}

	return converter.ConvertToolcallsStats(stats), nil
}

// ToolcallsStatsByPeriod is the resolver for the toolcallsStatsByPeriod field.
func (r *queryResolver) ToolcallsStatsByPeriod(ctx context.Context, period model.UsageStatsPeriod, timezone *string) ([]*model.DailyToolcallsStats, error) {
	uid, _, err := validatePermission(ctx, "usage.view")
	if err != nil {
		return nil, err
	}

	days, err := periodDays(period)
	if err != nil {
		return nil, err
	}

	tz, err := r.Timezones.ResolveFor(timezone)
	if err != nil {
		return nil, err
	}

	r.Logger.WithFields(logrus.Fields{
		"uid":      uid,
		"period":   period,
		"timezone": tz,
	}).Debug("get toolcalls stats by period")

	stats, err := r.DB.GetToolcallsStatsByDay(ctx, database.GetToolcallsStatsByDayParams{
		UserID: uid,
		Tz:     tz,
		Days:   days,
	})
	if err != nil {
		return nil, err
	}

	return converter.ConvertDailyToolcallsStats(stats), nil
}

// ToolcallsStatsByFunction is the resolver for the toolcallsStatsByFunction field.
func (r *queryResolver) ToolcallsStatsByFunction(ctx context.Context) ([]*model.FunctionToolcallsStats, error) {
	uid, _, err := validatePermission(ctx, "usage.view")
	if err != nil {
		return nil, err
	}

	r.Logger.WithFields(logrus.Fields{
		"uid": uid,
	}).Debug("get toolcalls stats by function")

	stats, err := r.DB.GetToolcallsStatsByFunction(ctx, uid)
	if err != nil {
		return nil, err
	}

	return converter.ConvertFunctionToolcallsStats(stats), nil
}

// ToolcallsStatsByFlow is the resolver for the toolcallsStatsByFlow field.
func (r *queryResolver) ToolcallsStatsByFlow(ctx context.Context, flowID int64) (*model.ToolcallsStats, error) {
	uid, err := validatePermissionWithFlowID(ctx, "usage.view", flowID, r.DB)
	if err != nil {
		return nil, err
	}

	r.Logger.WithFields(logrus.Fields{
		"uid":  uid,
		"flow": flowID,
	}).Debug("get toolcalls stats by flow")

	stats, err := r.DB.GetFlowToolcallsStats(ctx, flowID)
	if err != nil {
		return nil, err
	}

	return converter.ConvertToolcallsStats(stats), nil
}

// ToolcallsStatsByFunctionForFlow is the resolver for the toolcallsStatsByFunctionForFlow field.
func (r *queryResolver) ToolcallsStatsByFunctionForFlow(ctx context.Context, flowID int64) ([]*model.FunctionToolcallsStats, error) {
	uid, err := validatePermissionWithFlowID(ctx, "usage.view", flowID, r.DB)
	if err != nil {
		return nil, err
	}

	r.Logger.WithFields(logrus.Fields{
		"uid":  uid,
		"flow": flowID,
	}).Debug("get toolcalls stats by function for flow")

	stats, err := r.DB.GetToolcallsStatsByFunctionForFlow(ctx, flowID)
	if err != nil {
		return nil, err
	}

	return converter.ConvertFunctionToolcallsStatsForFlow(stats), nil
}

// FlowsStatsTotal is the resolver for the flowsStatsTotal field.
func (r *queryResolver) FlowsStatsTotal(ctx context.Context) (*model.FlowsStats, error) {
	uid, _, err := validatePermission(ctx, "usage.view")
	if err != nil {
		return nil, err
	}

	r.Logger.WithFields(logrus.Fields{
		"uid": uid,
	}).Debug("get total flows stats")

	stats, err := r.DB.GetUserTotalFlowsStats(ctx, uid)
	if err != nil {
		return nil, err
	}

	return converter.ConvertFlowsStats(stats), nil
}

// FlowsStatsByPeriod is the resolver for the flowsStatsByPeriod field.
func (r *queryResolver) FlowsStatsByPeriod(ctx context.Context, period model.UsageStatsPeriod, timezone *string) ([]*model.DailyFlowsStats, error) {
	uid, _, err := validatePermission(ctx, "usage.view")
	if err != nil {
		return nil, err
	}

	days, err := periodDays(period)
	if err != nil {
		return nil, err
	}

	tz, err := r.Timezones.ResolveFor(timezone)
	if err != nil {
		return nil, err
	}

	r.Logger.WithFields(logrus.Fields{
		"uid":      uid,
		"period":   period,
		"timezone": tz,
	}).Debug("get flows stats by period")

	stats, err := r.DB.GetFlowsStatsByDay(ctx, database.GetFlowsStatsByDayParams{
		UserID: uid,
		Tz:     tz,
		Days:   days,
	})
	if err != nil {
		return nil, err
	}

	return converter.ConvertDailyFlowsStats(stats), nil
}

// FlowStatsByFlow is the resolver for the flowStatsByFlow field.
func (r *queryResolver) FlowStatsByFlow(ctx context.Context, flowID int64) (*model.FlowStats, error) {
	uid, err := validatePermissionWithFlowID(ctx, "usage.view", flowID, r.DB)
	if err != nil {
		return nil, err
	}

	r.Logger.WithFields(logrus.Fields{
		"uid":  uid,
		"flow": flowID,
	}).Debug("get flow stats by flow")

	stats, err := r.DB.GetFlowStats(ctx, flowID)
	if err != nil {
		return nil, err
	}

	return converter.ConvertFlowStats(stats), nil
}

// FlowsExecutionStatsByPeriod is the resolver for the flowsExecutionStatsByPeriod field.
func (r *queryResolver) FlowsExecutionStatsByPeriod(ctx context.Context, period model.UsageStatsPeriod, timezone *string) ([]*model.FlowExecutionStats, error) {
	uid, _, err := validatePermission(ctx, "usage.view")
	if err != nil {
		return nil, err
	}

	r.Logger.WithFields(logrus.Fields{
		"uid":    uid,
		"period": period,
	}).Debug("get flows execution stats by period")

	type flowInfo struct {
		ID    int64
		Title string
	}
	var flows []flowInfo

	days, err := periodDays(period)
	if err != nil {
		return nil, err
	}

	tz, err := r.Timezones.ResolveFor(timezone)
	if err != nil {
		return nil, err
	}

	rows, err := r.DB.GetFlowsForPeriod(ctx, database.GetFlowsForPeriodParams{
		UserID: uid,
		Tz:     tz,
		Days:   days,
	})
	if err != nil {
		return nil, err
	}

	for _, row := range rows {
		flows = append(flows, flowInfo{ID: row.ID, Title: row.Title})
	}

	result := make([]*model.FlowExecutionStats, 0, len(flows))

	for _, flow := range flows {
		tasks, err := r.DB.GetTasksForFlow(ctx, flow.ID)
		if err != nil {
			return nil, err
		}

		taskIDs := make([]int64, len(tasks))
		for i, task := range tasks {
			taskIDs[i] = task.ID
		}

		var subtasks []database.GetSubtasksForTasksRow
		if len(taskIDs) > 0 {
			subtasks, err = r.DB.GetSubtasksForTasks(ctx, taskIDs)
			if err != nil {
				return nil, err
			}
		}

		msgchains, err := r.DB.GetMsgchainsForFlow(ctx, flow.ID)
		if err != nil {
			return nil, err
		}

		toolcalls, err := r.DB.GetToolcallsForFlow(ctx, flow.ID)
		if err != nil {
			return nil, err
		}

		assistantsCount, err := r.DB.GetAssistantsCountForFlow(ctx, flow.ID)
		if err != nil {
			return nil, err
		}

		flowStats := converter.BuildFlowExecutionStats(flow.ID, flow.Title, tasks, subtasks, msgchains, toolcalls, int(assistantsCount))
		result = append(result, flowStats)
	}

	return result, nil
}

// Settings is the resolver for the settings field.
func (r *queryResolver) Settings(ctx context.Context) (*model.Settings, error) {
	_, _, err := validatePermission(ctx, "settings.view")
	if err != nil {
		return nil, err
	}

	settings := &model.Settings{
		Debug:              r.Config.Debug,
		AskUser:            r.Config.AskUser,
		Version:            version.GetBinaryVersion(),
		DockerInside:       r.Config.DockerInside,
		IsDevelopMode:      version.IsDevelopMode(),
		AssistantUseAgents: r.Config.AssistantUseAgents,
	}

	return settings, nil
}

// VersionInfo is the resolver for the versionInfo field.
func (r *queryResolver) VersionInfo(ctx context.Context) (*model.VersionInfo, error) {
	_, _, err := validatePermission(ctx, "settings.view")
	if err != nil {
		return nil, err
	}

	// No service means checks were never configured — an installation id that is
	// missing or malformed — which to whoever reads the badge is the same as
	// checks switched off. The build itself is still named either way.
	status := update.DisabledStatus(r.Config)
	if r.Updates != nil {
		status = r.Updates.Status()
	}

	return versionInfoFromStatus(status), nil
}

// SettingsProviders is the resolver for the settingsProviders field.
func (r *queryResolver) SettingsProviders(ctx context.Context) (*model.ProvidersConfig, error) {
	uid, _, err := validatePermission(ctx, "settings.providers.view")
	if err != nil {
		return nil, err
	}

	r.Logger.WithFields(logrus.Fields{
		"uid": uid,
	}).Debug("get providers")

	if err := r.ProvidersCtrl.SeedDefaultProviders(ctx, uid); err != nil {
		r.Logger.WithError(err).Warn("failed to seed default providers")
	}

	config := model.ProvidersConfig{
		Enabled:     &model.ProvidersReadinessStatus{},
		Default:     &model.DefaultProvidersConfig{},
		Models:      &model.ProvidersModelsList{},
		UserDefined: make([]*model.ProviderConfig, 0),
	}

	now := time.Now()
	defaultProvidersConfig := r.ProvidersCtrl.DefaultProvidersConfig()
	for prvtype, pcfg := range defaultProvidersConfig {
		prefix := providers.ModelPrefix(r.Config, prvtype)
		mpcfg := &model.ProviderConfig{
			Name:      string(prvtype),
			Type:      model.ProviderType(prvtype),
			Agents:    converter.ConvertProviderConfigToGqlModel(pcfg),
			CreatedAt: now,
			UpdatedAt: now,
		}

		switch prvtype {
		case provider.ProviderOpenAI:
			config.Default.Openai = mpcfg
			if models, ok := providers.BundledCatalogs()[prvtype]; ok {
				config.Models.Openai = converter.ConvertModelsWithPrefix(models, prvtype.ReasoningProvider(), prefix)
			}
		case provider.ProviderAnthropic:
			config.Default.Anthropic = mpcfg
			if models, ok := providers.BundledCatalogs()[prvtype]; ok {
				config.Models.Anthropic = converter.ConvertModelsWithPrefix(models, prvtype.ReasoningProvider(), prefix)
			}
		case provider.ProviderGemini:
			config.Default.Gemini = mpcfg
			if models, ok := providers.BundledCatalogs()[prvtype]; ok {
				config.Models.Gemini = converter.ConvertModelsWithPrefix(models, prvtype.ReasoningProvider(), prefix)
			}
		case provider.ProviderBedrock:
			config.Default.Bedrock = mpcfg
			if models, ok := providers.BundledCatalogs()[prvtype]; ok {
				config.Models.Bedrock = converter.ConvertModelsWithPrefix(models, prvtype.ReasoningProvider(), prefix)
			}
		case provider.ProviderOllama:
			config.Default.Ollama = mpcfg
		case provider.ProviderCustom:
			config.Default.Custom = mpcfg
		case provider.ProviderDeepSeek:
			config.Default.Deepseek = mpcfg
			if models, ok := providers.BundledCatalogs()[prvtype]; ok {
				config.Models.Deepseek = converter.ConvertModelsWithPrefix(models, prvtype.ReasoningProvider(), prefix)
			}
		case provider.ProviderGLM:
			config.Default.Glm = mpcfg
			if models, ok := providers.BundledCatalogs()[prvtype]; ok {
				config.Models.Glm = converter.ConvertModelsWithPrefix(models, prvtype.ReasoningProvider(), prefix)
			}
		case provider.ProviderKimi:
			config.Default.Kimi = mpcfg
			if models, ok := providers.BundledCatalogs()[prvtype]; ok {
				config.Models.Kimi = converter.ConvertModelsWithPrefix(models, prvtype.ReasoningProvider(), prefix)
			}
		case provider.ProviderQwen:
			config.Default.Qwen = mpcfg
			if models, ok := providers.BundledCatalogs()[prvtype]; ok {
				config.Models.Qwen = converter.ConvertModelsWithPrefix(models, prvtype.ReasoningProvider(), prefix)
			}
		case provider.ProviderMiniMax:
			config.Default.Minimax = mpcfg
			if models, ok := providers.BundledCatalogs()[prvtype]; ok {
				config.Models.Minimax = converter.ConvertModelsWithPrefix(models, prvtype.ReasoningProvider(), prefix)
			}
		case provider.ProviderMistral:
			config.Default.Mistral = mpcfg
			if models, ok := providers.BundledCatalogs()[prvtype]; ok {
				config.Models.Mistral = converter.ConvertModelsWithPrefix(models, prvtype.ReasoningProvider(), prefix)
			}
		case provider.ProviderXAI:
			config.Default.Xai = mpcfg
			if models, ok := providers.BundledCatalogs()[prvtype]; ok {
				config.Models.Xai = converter.ConvertModelsWithPrefix(models, prvtype.ReasoningProvider(), prefix)
			}
		}
	}

	defaultProviders := r.ProvidersCtrl.DefaultProviders()
	for _, prvtype := range defaultProviders.ListTypes() {
		prefix := providers.ModelPrefix(r.Config, prvtype)
		switch prvtype {
		case provider.ProviderOpenAI:
			config.Enabled.Openai = true
		case provider.ProviderAnthropic:
			config.Enabled.Anthropic = true
		case provider.ProviderGemini:
			config.Enabled.Gemini = true
		case provider.ProviderBedrock:
			config.Enabled.Bedrock = true
			if p, ok := defaultProviders[provider.DefaultProviderNameBedrock]; ok {
				config.Models.Bedrock = converter.ConvertModelsWithPrefix(p.GetModels(), prvtype.ReasoningProvider(), prefix)
			}
		case provider.ProviderOllama:
			config.Enabled.Ollama = true
			if p, ok := defaultProviders[provider.DefaultProviderNameOllama]; ok {
				config.Models.Ollama = converter.ConvertModelsWithPrefix(p.GetModels(), prvtype.ReasoningProvider(), prefix)
			}
		case provider.ProviderCustom:
			config.Enabled.Custom = true
			if p, ok := defaultProviders[provider.DefaultProviderNameCustom]; ok {
				config.Models.Custom = converter.ConvertModelsWithPrefix(p.GetModels(), prvtype.ReasoningProvider(), prefix)
			}
		case provider.ProviderDeepSeek:
			config.Enabled.Deepseek = true
		case provider.ProviderGLM:
			config.Enabled.Glm = true
		case provider.ProviderKimi:
			config.Enabled.Kimi = true
		case provider.ProviderQwen:
			config.Enabled.Qwen = true
		case provider.ProviderMiniMax:
			config.Enabled.Minimax = true
		case provider.ProviderMistral:
			config.Enabled.Mistral = true
		case provider.ProviderXAI:
			config.Enabled.Xai = true
		}
	}

	providers, err := r.DB.GetUserProviders(ctx, uid)
	if err != nil {
		return nil, fmt.Errorf("failed to get user providers: %w", err)
	}

	for _, prv := range providers {
		var cfg pconfig.ProviderConfig

		if len(prv.Config) == 0 {
			prv.Config = []byte(pconfig.EmptyProviderConfigRaw)
		}
		if err := json.Unmarshal(prv.Config, &cfg); err != nil {
			r.Logger.WithError(err).Errorf("failed to unmarshal provider config: %s", prv.Config)
			continue
		}

		config.UserDefined = append(config.UserDefined, converter.ConvertProvider(prv, &cfg))
	}

	return &config, nil
}

// SettingsPrompts is the resolver for the settingsPrompts field.
func (r *queryResolver) SettingsPrompts(ctx context.Context) (*model.PromptsConfig, error) {
	uid, _, err := validatePermission(ctx, "settings.prompts.view")
	if err != nil {
		return nil, err
	}

	r.Logger.WithFields(logrus.Fields{
		"uid": uid,
	}).Debug("get prompts")

	prompts, err := r.DB.GetUserPrompts(ctx, uid)
	if err != nil {
		return nil, err
	}

	defaultPrompts, err := templates.GetDefaultPrompts()
	if err != nil {
		return nil, err
	}

	promptsConfig := model.PromptsConfig{
		Default:     converter.ConvertDefaultPrompts(defaultPrompts),
		UserDefined: make([]*model.UserPrompt, 0, len(prompts)),
	}

	for _, prompt := range prompts {
		promptsConfig.UserDefined = append(promptsConfig.UserDefined, converter.ConvertPrompt(prompt))
	}

	return &promptsConfig, nil
}

// SettingsUser is the resolver for the settingsUser field.
func (r *queryResolver) SettingsUser(ctx context.Context) (*model.UserPreferences, error) {
	uid, _, err := validatePermission(ctx, "settings.user.view")
	if err != nil {
		return nil, err
	}

	isUserSession, err := validateUserType(ctx, userSessionTypes...)
	if err != nil {
		return nil, err
	}

	if !isUserSession {
		return nil, fmt.Errorf("unauthorized: non-user session is not allowed to get user preferences")
	}

	r.Logger.WithFields(logrus.Fields{
		"uid": uid,
	}).Debug("get user preferences")

	prefs, err := r.DB.GetUserPreferencesByUserID(ctx, uid)
	if err != nil {
		if err == sql.ErrNoRows {
			return &model.UserPreferences{
				FavoriteFlows: []int64{},
			}, nil
		}
		return nil, fmt.Errorf("failed to get user preferences: %w", err)
	}

	return converter.ConvertUserPreferences(prefs), nil
}

// APIToken is the resolver for the apiToken field.
func (r *queryResolver) APIToken(ctx context.Context, tokenID string) (*model.APIToken, error) {
	uid, admin, err := validatePermission(ctx, "settings.tokens.view")
	if err != nil {
		return nil, err
	}

	isUserSession, err := validateUserType(ctx, userSessionTypes...)
	if err != nil {
		return nil, err
	}

	if !isUserSession {
		return nil, fmt.Errorf("unauthorized: non-user session is not allowed to get API tokens")
	}

	r.Logger.WithFields(logrus.Fields{
		"uid":     uid,
		"tokenID": tokenID,
	}).Debug("get api token")

	var token database.ApiToken

	if admin {
		token, err = r.DB.GetAPITokenByTokenID(ctx, tokenID)
	} else {
		token, err = r.DB.GetUserAPITokenByTokenID(ctx, database.GetUserAPITokenByTokenIDParams{
			TokenID: tokenID,
			UserID:  uid,
		})
	}
	if err != nil {
		return nil, fmt.Errorf("token not found: %w", err)
	}

	return converter.ConvertAPIToken(token), nil
}

// APITokens is the resolver for the apiTokens field.
func (r *queryResolver) APITokens(ctx context.Context) ([]*model.APIToken, error) {
	uid, admin, err := validatePermission(ctx, "settings.tokens.view")
	if err != nil {
		return nil, err
	}

	isUserSession, err := validateUserType(ctx, userSessionTypes...)
	if err != nil {
		return nil, err
	}

	if !isUserSession {
		return nil, fmt.Errorf("unauthorized: non-user session is not allowed to get API tokens")
	}

	r.Logger.WithFields(logrus.Fields{
		"uid": uid,
	}).Debug("get api tokens")

	var tokens []database.ApiToken

	if admin {
		tokens, err = r.DB.GetAPITokens(ctx)
	} else {
		tokens, err = r.DB.GetUserAPITokens(ctx, uid)
	}
	if err != nil {
		return nil, fmt.Errorf("failed to get tokens: %w", err)
	}

	return converter.ConvertAPITokens(tokens), nil
}

// FlowTemplate is the resolver for the flowTemplate field.
func (r *queryResolver) FlowTemplate(ctx context.Context, templateID int64) (*model.FlowTemplate, error) {
	uid, _, err := validatePermission(ctx, "templates.view")
	if err != nil {
		return nil, err
	}

	isUserSession, err := validateUserType(ctx, userSessionTypes...)
	if err != nil {
		return nil, err
	}

	if !isUserSession {
		return nil, fmt.Errorf("unauthorized: non-user session is not allowed to view templates")
	}

	r.Logger.WithFields(logrus.Fields{
		"uid":        uid,
		"templateID": templateID,
	}).Debug("get flow template")

	template, err := r.DB.GetFlowTemplate(ctx, database.GetFlowTemplateParams{
		ID:     templateID,
		UserID: uid,
	})
	if err != nil {
		if errors.Is(err, sql.ErrNoRows) {
			return nil, fmt.Errorf("template not found: %w", err)
		}
		return nil, fmt.Errorf("failed to get template: %w", err)
	}

	return converter.ConvertFlowTemplate(template), nil
}

// FlowTemplates is the resolver for the flowTemplates field.
func (r *queryResolver) FlowTemplates(ctx context.Context) ([]*model.FlowTemplate, error) {
	uid, _, err := validatePermission(ctx, "templates.view")
	if err != nil {
		return nil, err
	}

	isUserSession, err := validateUserType(ctx, userSessionTypes...)
	if err != nil {
		return nil, err
	}

	if !isUserSession {
		return nil, fmt.Errorf("unauthorized: non-user session is not allowed to view templates")
	}

	r.Logger.WithFields(logrus.Fields{
		"uid": uid,
	}).Debug("get flow templates")

	templates, err := r.DB.GetFlowTemplatesByUserID(ctx, uid)
	if err != nil {
		return nil, fmt.Errorf("failed to get templates: %w", err)
	}

	return converter.ConvertFlowTemplates(templates), nil
}

// Resources is the resolver for the resources field.
func (r *queryResolver) Resources(ctx context.Context, path *string, recursive *bool) ([]*model.UserResource, error) {
	uid, admin, err := validatePermission(ctx, "resources.view")
	if err != nil {
		return nil, err
	}

	r.Logger.WithFields(logrus.Fields{
		"uid":       uid,
		"admin":     admin,
		"path":      path,
		"recursive": recursive,
	}).Debug("list user resources")

	reqPath := ""
	if path != nil {
		reqPath = strings.TrimSpace(*path)
		if reqPath != "" {
			reqPath, err = resources.SanitizeResourcePath(reqPath)
			if err != nil {
				return nil, err
			}
		}
	}
	isRecursive := recursive != nil && *recursive

	var recs []database.UserResource

	if !admin {
		switch {
		case reqPath == "" && isRecursive:
			recs, err = r.DB.GetUserResourcesAll(ctx, uid)
		case reqPath == "":
			recs, err = r.DB.GetUserResourcesRoot(ctx, uid)
		case isRecursive:
			escaped := resources.EscapeLike(reqPath)
			recs, err = r.DB.GetUserResourcesRecursive(ctx, database.GetUserResourcesRecursiveParams{
				UserID:      uid,
				DirPath:     reqPath,
				ChildPrefix: escaped + "/%",
			})
		default:
			escaped := resources.EscapeLike(reqPath)
			recs, err = r.DB.GetUserResourcesInDir(ctx, database.GetUserResourcesInDirParams{
				UserID:      uid,
				DirPath:     reqPath,
				ChildPrefix: escaped + "/%",
				DeepPrefix:  escaped + "/%/%",
			})
		}
	} else {
		switch {
		case reqPath == "" && isRecursive:
			recs, err = r.DB.GetAllResourcesAll(ctx)
		case reqPath == "":
			recs, err = r.DB.GetAllResourcesRoot(ctx)
		case isRecursive:
			escaped := resources.EscapeLike(reqPath)
			recs, err = r.DB.GetAllResourcesRecursive(ctx, database.GetAllResourcesRecursiveParams{
				DirPath:     reqPath,
				ChildPrefix: escaped + "/%",
			})
		default:
			escaped := resources.EscapeLike(reqPath)
			recs, err = r.DB.GetAllResourcesInDir(ctx, database.GetAllResourcesInDirParams{
				DirPath:     reqPath,
				ChildPrefix: escaped + "/%",
				DeepPrefix:  escaped + "/%/%",
			})
		}
	}
	if err != nil {
		return nil, fmt.Errorf("failed to list resources: %w", err)
	}

	recs = resources.DedupeByPathPreferringOwner(recs, uid,
		func(rec database.UserResource) (string, int64) { return rec.Path, rec.UserID })

	return converter.ConvertUserResources(recs), nil
}

// KnowledgeDocuments is the resolver for the knowledgeDocuments field.
func (r *queryResolver) KnowledgeDocuments(ctx context.Context, filter *model.KnowledgeFilter, withContent bool) ([]*model.KnowledgeDocument, error) {
	uid, admin, err := validatePermission(ctx, "knowledge.view")
	if err != nil {
		return nil, err
	}

	r.Logger.WithFields(logrus.Fields{
		"uid":          uid,
		"admin":        admin,
		"with_content": withContent,
	}).Debug("list knowledge documents")

	if admin {
		return r.Knowledge.ListDocuments(ctx, filter, withContent)
	}
	return r.Knowledge.ListUserDocuments(ctx, uid, filter, withContent)
}

// KnowledgeDocument is the resolver for the knowledgeDocument field.
func (r *queryResolver) KnowledgeDocument(ctx context.Context, id string) (*model.KnowledgeDocument, error) {
	uid, admin, err := validatePermission(ctx, "knowledge.view")
	if err != nil {
		return nil, err
	}

	r.Logger.WithFields(logrus.Fields{
		"uid":   uid,
		"admin": admin,
		"id":    id,
	}).Debug("get knowledge document")

	if admin {
		return r.Knowledge.GetDocument(ctx, id)
	}
	return r.Knowledge.GetUserDocument(ctx, uid, id)
}

// SearchKnowledge is the resolver for the searchKnowledge field.
func (r *queryResolver) SearchKnowledge(ctx context.Context, query string, filter *model.KnowledgeFilter, limit *int) ([]*model.KnowledgeDocumentWithScore, error) {
	uid, admin, err := validatePermission(ctx, "knowledge.search")
	if err != nil {
		return nil, err
	}

	r.Logger.WithFields(logrus.Fields{
		"uid":   uid,
		"admin": admin,
		"query": query[:min(len(query), 200)],
	}).Debug("search knowledge documents")

	lim, err := validateKnowledgeSearch(query, limit)
	if err != nil {
		return nil, err
	}

	if admin {
		return r.Knowledge.SearchDocuments(ctx, query, filter, lim)
	}
	return r.Knowledge.SearchUserDocuments(ctx, uid, query, filter, lim)
}

// FlowCreated is the resolver for the flowCreated field.
func (r *subscriptionResolver) FlowCreated(ctx context.Context) (<-chan *model.Flow, error) {
	uid, admin, err := validatePermission(ctx, "flows.subscribe")
	if err != nil {
		return nil, err
	}

	subscriber := r.Subscriptions.NewFlowSubscriber(uid, 0)
	if admin {
		return subscriber.FlowCreatedAdmin(ctx)
	}

	return subscriber.FlowCreated(ctx)
}

// FlowDeleted is the resolver for the flowDeleted field.
func (r *subscriptionResolver) FlowDeleted(ctx context.Context) (<-chan *model.Flow, error) {
	uid, admin, err := validatePermission(ctx, "flows.subscribe")
	if err != nil {
		return nil, err
	}

	subscriber := r.Subscriptions.NewFlowSubscriber(uid, 0)
	if admin {
		return subscriber.FlowDeletedAdmin(ctx)
	}

	return subscriber.FlowDeleted(ctx)
}

// FlowUpdated is the resolver for the flowUpdated field.
func (r *subscriptionResolver) FlowUpdated(ctx context.Context) (<-chan *model.Flow, error) {
	uid, admin, err := validatePermission(ctx, "flows.subscribe")
	if err != nil {
		return nil, err
	}

	subscriber := r.Subscriptions.NewFlowSubscriber(uid, 0)
	if admin {
		return subscriber.FlowUpdatedAdmin(ctx)
	}

	return subscriber.FlowUpdated(ctx)
}

// TaskCreated is the resolver for the taskCreated field.
func (r *subscriptionResolver) TaskCreated(ctx context.Context, flowID int64) (<-chan *model.Task, error) {
	uid, err := validatePermissionWithFlowID(ctx, "tasks.subscribe", flowID, r.DB)
	if err != nil {
		return nil, err
	}

	return r.Subscriptions.NewFlowSubscriber(uid, flowID).TaskCreated(ctx)
}

// TaskUpdated is the resolver for the taskUpdated field.
func (r *subscriptionResolver) TaskUpdated(ctx context.Context, flowID int64) (<-chan *model.Task, error) {
	uid, err := validatePermissionWithFlowID(ctx, "tasks.subscribe", flowID, r.DB)
	if err != nil {
		return nil, err
	}

	return r.Subscriptions.NewFlowSubscriber(uid, flowID).TaskUpdated(ctx)
}

// AssistantCreated is the resolver for the assistantCreated field.
func (r *subscriptionResolver) AssistantCreated(ctx context.Context, flowID int64) (<-chan *model.Assistant, error) {
	uid, err := validatePermissionWithFlowID(ctx, "assistants.subscribe", flowID, r.DB)
	if err != nil {
		return nil, err
	}

	return r.Subscriptions.NewFlowSubscriber(uid, flowID).AssistantCreated(ctx)
}

// AssistantUpdated is the resolver for the assistantUpdated field.
func (r *subscriptionResolver) AssistantUpdated(ctx context.Context, flowID int64) (<-chan *model.Assistant, error) {
	uid, err := validatePermissionWithFlowID(ctx, "assistants.subscribe", flowID, r.DB)
	if err != nil {
		return nil, err
	}

	return r.Subscriptions.NewFlowSubscriber(uid, flowID).AssistantUpdated(ctx)
}

// AssistantDeleted is the resolver for the assistantDeleted field.
func (r *subscriptionResolver) AssistantDeleted(ctx context.Context, flowID int64) (<-chan *model.Assistant, error) {
	uid, err := validatePermissionWithFlowID(ctx, "assistants.subscribe", flowID, r.DB)
	if err != nil {
		return nil, err
	}

	return r.Subscriptions.NewFlowSubscriber(uid, flowID).AssistantDeleted(ctx)
}

// FlowFileAdded is the resolver for the flowFileAdded field.
func (r *subscriptionResolver) FlowFileAdded(ctx context.Context, flowID int64) (<-chan *model.FlowFile, error) {
	uid, err := validatePermissionWithFlowID(ctx, "flow_files.subscribe", flowID, r.DB)
	if err != nil {
		return nil, err
	}

	return r.Subscriptions.NewFlowSubscriber(uid, flowID).FlowFileAdded(ctx)
}

// FlowFileUpdated is the resolver for the flowFileUpdated field.
func (r *subscriptionResolver) FlowFileUpdated(ctx context.Context, flowID int64) (<-chan *model.FlowFile, error) {
	uid, err := validatePermissionWithFlowID(ctx, "flow_files.subscribe", flowID, r.DB)
	if err != nil {
		return nil, err
	}

	return r.Subscriptions.NewFlowSubscriber(uid, flowID).FlowFileUpdated(ctx)
}

// FlowFileDeleted is the resolver for the flowFileDeleted field.
func (r *subscriptionResolver) FlowFileDeleted(ctx context.Context, flowID int64) (<-chan *model.FlowFile, error) {
	uid, err := validatePermissionWithFlowID(ctx, "flow_files.subscribe", flowID, r.DB)
	if err != nil {
		return nil, err
	}

	return r.Subscriptions.NewFlowSubscriber(uid, flowID).FlowFileDeleted(ctx)
}

// ScreenshotAdded is the resolver for the screenshotAdded field.
func (r *subscriptionResolver) ScreenshotAdded(ctx context.Context, flowID int64) (<-chan *model.Screenshot, error) {
	uid, err := validatePermissionWithFlowID(ctx, "screenshots.subscribe", flowID, r.DB)
	if err != nil {
		return nil, err
	}

	return r.Subscriptions.NewFlowSubscriber(uid, flowID).ScreenshotAdded(ctx)
}

// TerminalLogAdded is the resolver for the terminalLogAdded field.
func (r *subscriptionResolver) TerminalLogAdded(ctx context.Context, flowID int64) (<-chan *model.TerminalLog, error) {
	uid, err := validatePermissionWithFlowID(ctx, "termlogs.subscribe", flowID, r.DB)
	if err != nil {
		return nil, err
	}

	return r.Subscriptions.NewFlowSubscriber(uid, flowID).TerminalLogAdded(ctx)
}

// MessageLogAdded is the resolver for the messageLogAdded field.
func (r *subscriptionResolver) MessageLogAdded(ctx context.Context, flowID int64) (<-chan *model.MessageLog, error) {
	uid, err := validatePermissionWithFlowID(ctx, "msglogs.subscribe", flowID, r.DB)
	if err != nil {
		return nil, err
	}

	return r.Subscriptions.NewFlowSubscriber(uid, flowID).MessageLogAdded(ctx)
}

// MessageLogUpdated is the resolver for the messageLogUpdated field.
func (r *subscriptionResolver) MessageLogUpdated(ctx context.Context, flowID int64) (<-chan *model.MessageLog, error) {
	uid, err := validatePermissionWithFlowID(ctx, "msglogs.subscribe", flowID, r.DB)
	if err != nil {
		return nil, err
	}

	return r.Subscriptions.NewFlowSubscriber(uid, flowID).MessageLogUpdated(ctx)
}

// AgentLogAdded is the resolver for the agentLogAdded field.
func (r *subscriptionResolver) AgentLogAdded(ctx context.Context, flowID int64) (<-chan *model.AgentLog, error) {
	uid, err := validatePermissionWithFlowID(ctx, "agentlogs.subscribe", flowID, r.DB)
	if err != nil {
		return nil, err
	}

	return r.Subscriptions.NewFlowSubscriber(uid, flowID).AgentLogAdded(ctx)
}

// SearchLogAdded is the resolver for the searchLogAdded field.
func (r *subscriptionResolver) SearchLogAdded(ctx context.Context, flowID int64) (<-chan *model.SearchLog, error) {
	uid, err := validatePermissionWithFlowID(ctx, "searchlogs.subscribe", flowID, r.DB)
	if err != nil {
		return nil, err
	}

	return r.Subscriptions.NewFlowSubscriber(uid, flowID).SearchLogAdded(ctx)
}

// VectorStoreLogAdded is the resolver for the vectorStoreLogAdded field.
func (r *subscriptionResolver) VectorStoreLogAdded(ctx context.Context, flowID int64) (<-chan *model.VectorStoreLog, error) {
	uid, err := validatePermissionWithFlowID(ctx, "vecstorelogs.subscribe", flowID, r.DB)
	if err != nil {
		return nil, err
	}

	return r.Subscriptions.NewFlowSubscriber(uid, flowID).VectorStoreLogAdded(ctx)
}

// ToolCallLogAdded is the resolver for the toolCallLogAdded field.
func (r *subscriptionResolver) ToolCallLogAdded(ctx context.Context, flowID int64) (<-chan *model.ToolCallLog, error) {
	uid, err := validatePermissionWithFlowID(ctx, "toolcalls.subscribe", flowID, r.DB)
	if err != nil {
		return nil, err
	}

	return r.Subscriptions.NewFlowSubscriber(uid, flowID).ToolCallLogAdded(ctx)
}

// ToolCallLogUpdated is the resolver for the toolCallLogUpdated field.
func (r *subscriptionResolver) ToolCallLogUpdated(ctx context.Context, flowID int64) (<-chan *model.ToolCallLog, error) {
	uid, err := validatePermissionWithFlowID(ctx, "toolcalls.subscribe", flowID, r.DB)
	if err != nil {
		return nil, err
	}

	return r.Subscriptions.NewFlowSubscriber(uid, flowID).ToolCallLogUpdated(ctx)
}

// AssistantLogAdded is the resolver for the assistantLogAdded field.
func (r *subscriptionResolver) AssistantLogAdded(ctx context.Context, flowID int64) (<-chan *model.AssistantLog, error) {
	uid, err := validatePermissionWithFlowID(ctx, "assistantlogs.subscribe", flowID, r.DB)
	if err != nil {
		return nil, err
	}

	return r.Subscriptions.NewFlowSubscriber(uid, flowID).AssistantLogAdded(ctx)
}

// AssistantLogUpdated is the resolver for the assistantLogUpdated field.
func (r *subscriptionResolver) AssistantLogUpdated(ctx context.Context, flowID int64) (<-chan *model.AssistantLog, error) {
	uid, err := validatePermissionWithFlowID(ctx, "assistantlogs.subscribe", flowID, r.DB)
	if err != nil {
		return nil, err
	}

	return r.Subscriptions.NewFlowSubscriber(uid, flowID).AssistantLogUpdated(ctx)
}

// ProviderCreated is the resolver for the providerCreated field.
func (r *subscriptionResolver) ProviderCreated(ctx context.Context) (<-chan *model.ProviderConfig, error) {
	uid, _, err := validatePermission(ctx, "settings.providers.subscribe")
	if err != nil {
		return nil, err
	}

	return r.Subscriptions.NewProviderSubscriber(uid).ProviderCreated(ctx)
}

// ProviderUpdated is the resolver for the providerUpdated field.
func (r *subscriptionResolver) ProviderUpdated(ctx context.Context) (<-chan *model.ProviderConfig, error) {
	uid, _, err := validatePermission(ctx, "settings.providers.subscribe")
	if err != nil {
		return nil, err
	}

	return r.Subscriptions.NewProviderSubscriber(uid).ProviderUpdated(ctx)
}

// ProviderDeleted is the resolver for the providerDeleted field.
func (r *subscriptionResolver) ProviderDeleted(ctx context.Context) (<-chan *model.ProviderConfig, error) {
	uid, _, err := validatePermission(ctx, "settings.providers.subscribe")
	if err != nil {
		return nil, err
	}

	return r.Subscriptions.NewProviderSubscriber(uid).ProviderDeleted(ctx)
}

// APITokenCreated is the resolver for the apiTokenCreated field.
func (r *subscriptionResolver) APITokenCreated(ctx context.Context) (<-chan *model.APIToken, error) {
	uid, _, err := validatePermission(ctx, "settings.tokens.subscribe")
	if err != nil {
		return nil, err
	}

	isUserSession, err := validateUserType(ctx, userSessionTypes...)
	if err != nil {
		return nil, err
	}

	if !isUserSession {
		return nil, fmt.Errorf("unauthorized: non-user session is not allowed to subscribe to API tokens")
	}

	return r.Subscriptions.NewAPITokenSubscriber(uid).APITokenCreated(ctx)
}

// APITokenUpdated is the resolver for the apiTokenUpdated field.
func (r *subscriptionResolver) APITokenUpdated(ctx context.Context) (<-chan *model.APIToken, error) {
	uid, _, err := validatePermission(ctx, "settings.tokens.subscribe")
	if err != nil {
		return nil, err
	}

	isUserSession, err := validateUserType(ctx, userSessionTypes...)
	if err != nil {
		return nil, err
	}

	if !isUserSession {
		return nil, fmt.Errorf("unauthorized: non-user session is not allowed to subscribe to API tokens")
	}

	return r.Subscriptions.NewAPITokenSubscriber(uid).APITokenUpdated(ctx)
}

// APITokenDeleted is the resolver for the apiTokenDeleted field.
func (r *subscriptionResolver) APITokenDeleted(ctx context.Context) (<-chan *model.APIToken, error) {
	uid, _, err := validatePermission(ctx, "settings.tokens.subscribe")
	if err != nil {
		return nil, err
	}

	isUserSession, err := validateUserType(ctx, userSessionTypes...)
	if err != nil {
		return nil, err
	}

	if !isUserSession {
		return nil, fmt.Errorf("unauthorized: non-user session is not allowed to subscribe to API tokens")
	}

	return r.Subscriptions.NewAPITokenSubscriber(uid).APITokenDeleted(ctx)
}

// SettingsUserUpdated is the resolver for the settingsUserUpdated field.
func (r *subscriptionResolver) SettingsUserUpdated(ctx context.Context) (<-chan *model.UserPreferences, error) {
	uid, _, err := validatePermission(ctx, "settings.user.subscribe")
	if err != nil {
		return nil, err
	}

	isUserSession, err := validateUserType(ctx, userSessionTypes...)
	if err != nil {
		return nil, err
	}

	if !isUserSession {
		return nil, fmt.Errorf("unauthorized: non-user session is not allowed to subscribe to user preferences")
	}

	return r.Subscriptions.NewSettingsSubscriber(uid).SettingsUserUpdated(ctx)
}

// FlowTemplateCreated is the resolver for the flowTemplateCreated field.
func (r *subscriptionResolver) FlowTemplateCreated(ctx context.Context) (<-chan *model.FlowTemplate, error) {
	uid, _, err := validatePermission(ctx, "templates.subscribe")
	if err != nil {
		return nil, err
	}

	isUserSession, err := validateUserType(ctx, userSessionTypes...)
	if err != nil {
		return nil, err
	}

	if !isUserSession {
		return nil, fmt.Errorf("unauthorized: non-user session is not allowed to subscribe to templates")
	}

	return r.Subscriptions.NewFlowTemplateSubscriber(uid).FlowTemplateCreated(ctx)
}

// FlowTemplateUpdated is the resolver for the flowTemplateUpdated field.
func (r *subscriptionResolver) FlowTemplateUpdated(ctx context.Context) (<-chan *model.FlowTemplate, error) {
	uid, _, err := validatePermission(ctx, "templates.subscribe")
	if err != nil {
		return nil, err
	}

	isUserSession, err := validateUserType(ctx, userSessionTypes...)
	if err != nil {
		return nil, err
	}

	if !isUserSession {
		return nil, fmt.Errorf("unauthorized: non-user session is not allowed to subscribe to templates")
	}

	return r.Subscriptions.NewFlowTemplateSubscriber(uid).FlowTemplateUpdated(ctx)
}

// FlowTemplateDeleted is the resolver for the flowTemplateDeleted field.
func (r *subscriptionResolver) FlowTemplateDeleted(ctx context.Context) (<-chan *model.FlowTemplate, error) {
	uid, _, err := validatePermission(ctx, "templates.subscribe")
	if err != nil {
		return nil, err
	}

	isUserSession, err := validateUserType(ctx, userSessionTypes...)
	if err != nil {
		return nil, err
	}

	if !isUserSession {
		return nil, fmt.Errorf("unauthorized: non-user session is not allowed to subscribe to templates")
	}

	return r.Subscriptions.NewFlowTemplateSubscriber(uid).FlowTemplateDeleted(ctx)
}

// ResourceAdded is the resolver for the resourceAdded field.
func (r *subscriptionResolver) ResourceAdded(ctx context.Context) (<-chan *model.UserResource, error) {
	uid, admin, err := validatePermission(ctx, "resources.subscribe")
	if err != nil {
		return nil, err
	}

	sub := r.Subscriptions.NewResourceSubscriber(uid)
	if admin {
		return sub.ResourceAddedAdmin(ctx)
	}
	return sub.ResourceAdded(ctx)
}

// ResourceUpdated is the resolver for the resourceUpdated field.
func (r *subscriptionResolver) ResourceUpdated(ctx context.Context) (<-chan *model.UserResource, error) {
	uid, admin, err := validatePermission(ctx, "resources.subscribe")
	if err != nil {
		return nil, err
	}

	sub := r.Subscriptions.NewResourceSubscriber(uid)
	if admin {
		return sub.ResourceUpdatedAdmin(ctx)
	}
	return sub.ResourceUpdated(ctx)
}

// ResourceDeleted is the resolver for the resourceDeleted field.
func (r *subscriptionResolver) ResourceDeleted(ctx context.Context) (<-chan *model.UserResource, error) {
	uid, admin, err := validatePermission(ctx, "resources.subscribe")
	if err != nil {
		return nil, err
	}

	sub := r.Subscriptions.NewResourceSubscriber(uid)
	if admin {
		return sub.ResourceDeletedAdmin(ctx)
	}
	return sub.ResourceDeleted(ctx)
}

// KnowledgeDocumentCreated is the resolver for the knowledgeDocumentCreated field.
func (r *subscriptionResolver) KnowledgeDocumentCreated(ctx context.Context) (<-chan *model.KnowledgeDocument, error) {
	uid, admin, err := validatePermission(ctx, "knowledge.subscribe")
	if err != nil {
		return nil, err
	}

	sub := r.Subscriptions.NewKnowledgeSubscriber(uid)
	if admin {
		return sub.KnowledgeDocumentCreatedAdmin(ctx)
	}
	return sub.KnowledgeDocumentCreated(ctx)
}

// KnowledgeDocumentUpdated is the resolver for the knowledgeDocumentUpdated field.
func (r *subscriptionResolver) KnowledgeDocumentUpdated(ctx context.Context) (<-chan *model.KnowledgeDocument, error) {
	uid, admin, err := validatePermission(ctx, "knowledge.subscribe")
	if err != nil {
		return nil, err
	}

	sub := r.Subscriptions.NewKnowledgeSubscriber(uid)
	if admin {
		return sub.KnowledgeDocumentUpdatedAdmin(ctx)
	}
	return sub.KnowledgeDocumentUpdated(ctx)
}

// KnowledgeDocumentDeleted is the resolver for the knowledgeDocumentDeleted field.
func (r *subscriptionResolver) KnowledgeDocumentDeleted(ctx context.Context) (<-chan *model.KnowledgeDocument, error) {
	uid, admin, err := validatePermission(ctx, "knowledge.subscribe")
	if err != nil {
		return nil, err
	}

	sub := r.Subscriptions.NewKnowledgeSubscriber(uid)
	if admin {
		return sub.KnowledgeDocumentDeletedAdmin(ctx)
	}
	return sub.KnowledgeDocumentDeleted(ctx)
}

// Mutation returns MutationResolver implementation.
func (r *Resolver) Mutation() MutationResolver { return &mutationResolver{r} }

// Query returns QueryResolver implementation.
func (r *Resolver) Query() QueryResolver { return &queryResolver{r} }

// Subscription returns SubscriptionResolver implementation.
func (r *Resolver) Subscription() SubscriptionResolver { return &subscriptionResolver{r} }

type mutationResolver struct{ *Resolver }
type queryResolver struct{ *Resolver }
type subscriptionResolver struct{ *Resolver }
