package graph_resolver

// 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.76

import (
	"context"
	"encoding/json"
	"errors"
	"fmt"
	"os"
	"path/filepath"
	"time"

	"github.com/anyshake/observer/config"
	"github.com/anyshake/observer/internal/dao/model"
	graph_model "github.com/anyshake/observer/internal/server/router/graph/model"
	"github.com/anyshake/observer/internal/service"
	service_helicorder "github.com/anyshake/observer/internal/service/helicorder"
	service_miniseed "github.com/anyshake/observer/internal/service/miniseed"
	"github.com/anyshake/observer/pkg/seisevent"
	"github.com/anyshake/observer/pkg/system"
	"github.com/samber/lo"
)

// CreateSysUser is the resolver for the createSysUser field.
func (r *mutationResolver) CreateSysUser(ctx context.Context, username string, password string, admin bool) (string, error) {
	if !r.checkIsAdmin(ctx) {
		return "", errors.New("permission denied")
	}

	if err := r.ActionHandler.SysUserCheckPassword(password); err != nil {
		return "", err
	}

	userId, err := r.ActionHandler.SysUserCreate(username, password, admin)
	if err != nil {
		return "", fmt.Errorf("failed to create user: %w", err)
	}

	return userId, nil
}

// RemoveSysUser is the resolver for the removeSysUser field.
func (r *mutationResolver) RemoveSysUser(ctx context.Context, userID string) (bool, error) {
	if !r.checkIsAdmin(ctx) {
		return false, errors.New("permission denied")
	}

	currentUserId := r.getCurrentUserId(ctx)
	if currentUserId == userID {
		return false, fmt.Errorf("current user cannot be removed, user ID: %s", userID)
	}

	if err := r.ActionHandler.SysUserRemove(userID); err != nil {
		return false, fmt.Errorf("failed to remove user: %w", err)
	}

	return true, nil
}

// UpdateSysUser is the resolver for the updateSysUser field.
func (r *mutationResolver) UpdateSysUser(ctx context.Context, userID string, username string, password *string, admin bool) (bool, error) {
	if !r.checkIsAdmin(ctx) {
		return false, errors.New("permission denied")
	}

	user := model.SysUser{UserId: userID}

	if err := r.ActionHandler.SysUserCheckUsername(username); err != nil {
		return false, err
	}
	user.Username = username

	if password != nil {
		if err := r.ActionHandler.SysUserCheckPassword(*password); err != nil {
			return false, err
		}
		user.HashedPassword = user.GetHashedPassword(*password)
	}

	// Note that the admin itself cannot downgrade to a regular user
	if r.checkIsAdmin(ctx) && userID == r.getCurrentUserId(ctx) && !admin {
		return false, errors.New("current admin itself cannot be downgraded")
	}
	user.IsAdmin = lo.Ternary(admin, model.ADMIN, model.NON_ADMIN)

	if err := r.ActionHandler.SysUserUpdte(userID, user); err != nil {
		return false, fmt.Errorf("failed to update user: %w", err)
	}

	return true, nil
}

// PurgeSeisRecords is the resolver for the purgeSeisRecords field.
func (r *mutationResolver) PurgeSeisRecords(ctx context.Context) (bool, error) {
	if !r.checkIsAdmin(ctx) {
		return false, errors.New("permission denied")
	}

	if err := r.ActionHandler.SeisRecordsPurge(time.Unix(0, 0), r.TimeSource.Now()); err != nil {
		return false, fmt.Errorf("failed to purge seis records: %w", err)
	}

	return true, nil
}

// PurgeMiniSeedFiles is the resolver for the purgeMiniSeedFiles field.
func (r *mutationResolver) PurgeMiniSeedFiles(ctx context.Context) (bool, error) {
	if !r.checkIsAdmin(ctx) {
		return false, errors.New("permission denied")
	}

	serviceObj, ok := r.ServiceMap[service_miniseed.ID]
	if !ok {
		return false, fmt.Errorf("service was not found, maybe it was excluded from building")
	}
	assets, err := serviceObj.GetAssetList()
	if err != nil {
		return false, fmt.Errorf("failed to get assets list for MiniSEED service: %w", err)
	}

	for _, asset := range assets {
		dirPath := filepath.Dir(asset.FilePath)
		if err := os.RemoveAll(dirPath); err != nil {
			return false, fmt.Errorf("failed to remove directory %s: %w", dirPath, err)
		}
	}

	return true, nil
}

// PurgeHelicorderFiles is the resolver for the purgeHelicorderFiles field.
func (r *mutationResolver) PurgeHelicorderFiles(ctx context.Context) (bool, error) {
	if !r.checkIsAdmin(ctx) {
		return false, errors.New("permission denied")
	}

	serviceObj, ok := r.ServiceMap[service_helicorder.ID]
	if !ok {
		return false, fmt.Errorf("service was not found, maybe it was excluded from building")
	}
	assets, err := serviceObj.GetAssetList()
	if err != nil {
		return false, fmt.Errorf("failed to get assets list for helicorder service: %w", err)
	}

	for _, asset := range assets {
		dirPath := filepath.Dir(asset.FilePath)
		if err := os.RemoveAll(dirPath); err != nil {
			return false, fmt.Errorf("failed to remove directory %s: %w", dirPath, err)
		}
	}

	return true, nil
}

// UpdateStationConfig is the resolver for the updateStationConfig field.
func (r *mutationResolver) UpdateStationConfig(ctx context.Context, key string, value any) (bool, error) {
	if !r.checkIsAdmin(ctx) {
		return false, errors.New("permission denied")
	}

	constraint, ok := lo.Find(r.StationConfigConstraints, func(constraint config.IConstraint) bool {
		return constraint.GetKey() == key
	})
	if ok {
		if err := constraint.Set(r.ActionHandler, value); err != nil {
			return false, fmt.Errorf("failed to update station config, namespace %s, key %s: %w", constraint.GetNamespace(), constraint.GetKey(), err)
		}
		return true, nil
	}

	return false, fmt.Errorf("key %s not found in station settings", key)
}

// RestoreStationConfig is the resolver for the restoreStationConfig field.
func (r *mutationResolver) RestoreStationConfig(ctx context.Context) (bool, error) {
	if !r.checkIsAdmin(ctx) {
		return false, errors.New("permission denied")
	}

	for _, obj := range r.StationConfigConstraints {
		if err := obj.Restore(r.ActionHandler); err != nil {
			return false, fmt.Errorf("failed to restore station config, key %s: %w", obj.GetKey(), err)
		}
	}

	return true, nil
}

// ImportGlobalConfig is the resolver for the importGlobalConfig field.
func (r *mutationResolver) ImportGlobalConfig(ctx context.Context, data string) (bool, error) {
	if !r.checkIsAdmin(ctx) {
		return false, errors.New("permission denied")
	}

	var globalConfig map[string]map[string]map[string]any
	if err := json.Unmarshal([]byte(data), &globalConfig); err != nil {
		return false, fmt.Errorf("failed to unmarshal config data: %w", err)
	}
	if len(globalConfig) == 0 {
		return false, errors.New("config data is empty")
	}

	for _, constraint := range r.StationConfigConstraints {
		configNamespace, ok := globalConfig[config.STATION_NAMESPACE]
		if !ok {
			continue
		}
		configObj, ok := configNamespace[constraint.GetKey()]
		if !ok {
			continue
		}

		dataType, ok := configObj["type"].(string)
		if !ok || dataType != string(constraint.GetType()) {
			continue
		}
		version, err := config.GetConfigValInt64(configObj["version"])
		if err != nil && version != int64(constraint.GetVersion()) {
			continue
		}
		value, ok := configObj["value"]
		if !ok {
			continue
		}

		if err := constraint.Set(r.ActionHandler, value); err != nil {
			return false, fmt.Errorf("failed to import station config, key %s: %w", constraint.GetKey(), err)
		}
	}

	for namespace, serviceObj := range r.ServiceMap {
		serviceConfig, ok := globalConfig[namespace]
		if !ok {
			continue
		}

		for _, constraint := range serviceObj.GetConfigConstraint() {
			configObj, ok := serviceConfig[constraint.GetKey()]
			if !ok {
				continue
			}

			dataType, ok := configObj["type"].(string)
			if !ok || dataType != string(constraint.GetType()) {
				continue
			}
			version, err := config.GetConfigValInt64(configObj["version"])
			if err != nil && version != int64(constraint.GetVersion()) {
				continue
			}
			value, ok := configObj["value"]
			if !ok {
				continue
			}

			if err := constraint.Set(r.ActionHandler, value); err != nil {
				return false, fmt.Errorf("failed to import service config, namespace %s, key %s: %w", namespace, constraint.GetKey(), err)
			}
		}
	}

	return true, nil
}

// StopService is the resolver for the stopService field.
func (r *mutationResolver) StopService(ctx context.Context, serviceID string) (bool, error) {
	if !r.checkIsAdmin(ctx) {
		return false, errors.New("permission denied")
	}

	ser, ok := r.ServiceMap[serviceID]
	if !ok {
		return false, fmt.Errorf("service ID %s not found", serviceID)
	}
	if !ser.GetStatus().GetIsRunning() {
		return false, fmt.Errorf("service ID %s is not running", serviceID)
	}
	if err := ser.Stop(); err != nil {
		return false, fmt.Errorf("failed to stop service ID %s: %w", serviceID, err)
	}

	return true, nil
}

// StartService is the resolver for the startService field.
func (r *mutationResolver) StartService(ctx context.Context, serviceID string) (bool, error) {
	if !r.checkIsAdmin(ctx) {
		return false, errors.New("permission denied")
	}

	ser, ok := r.ServiceMap[serviceID]
	if !ok {
		return false, fmt.Errorf("service ID %s not found", serviceID)
	}
	if ser.GetStatus().GetIsRunning() {
		return false, fmt.Errorf("service ID %s is already running", serviceID)
	}
	if err := ser.Init(); err != nil {
		return false, fmt.Errorf("failed to initialize service ID %s: %w", serviceID, err)
	}
	if err := ser.Start(); err != nil {
		return false, fmt.Errorf("failed to start service ID %s: %w", serviceID, err)
	}

	return true, nil
}

// RestartService is the resolver for the restartService field.
func (r *mutationResolver) RestartService(ctx context.Context, serviceID string) (bool, error) {
	if !r.checkIsAdmin(ctx) {
		return false, errors.New("permission denied")
	}

	ser, ok := r.ServiceMap[serviceID]
	if !ok {
		return false, fmt.Errorf("service ID %s not found", serviceID)
	}
	if err := ser.Restart(); err != nil {
		return false, fmt.Errorf("failed to restart service ID %s: %w", serviceID, err)
	}

	return true, nil
}

// RestoreServiceConfig is the resolver for the restoreServiceConfig field.
func (r *mutationResolver) RestoreServiceConfig(ctx context.Context, serviceID *string) (bool, error) {
	if !r.checkIsAdmin(ctx) {
		return false, errors.New("permission denied")
	}

	if serviceID != nil {
		serviceObj, ok := r.ServiceMap[*serviceID]
		if !ok {
			return false, fmt.Errorf("service ID %s not found", *serviceID)
		}

		for _, cfg := range serviceObj.GetConfigConstraint() {
			if err := cfg.Restore(r.ActionHandler); err != nil {
				return false, fmt.Errorf("failed to restore service config, service ID %s, namespace %s, key %s: %w", *serviceID, cfg.GetNamespace(), cfg.GetKey(), err)
			}
		}
	} else {
		for serviceId, serviceObj := range r.ServiceMap {
			for _, cfg := range serviceObj.GetConfigConstraint() {
				if err := cfg.Restore(r.ActionHandler); err != nil {
					return false, fmt.Errorf("failed to restore service config, service ID %s, namespace %s, key %s: %w", serviceId, cfg.GetNamespace(), cfg.GetKey(), err)
				}
			}
		}
	}

	return true, nil
}

// UpdateServiceConfig is the resolver for the updateServiceConfig field.
func (r *mutationResolver) UpdateServiceConfig(ctx context.Context, serviceID string, key string, val any) (bool, error) {
	if !r.checkIsAdmin(ctx) {
		return false, errors.New("permission denied")
	}

	serviceObj, ok := r.ServiceMap[serviceID]
	if !ok {
		return false, fmt.Errorf("service ID %s not found", serviceID)
	}

	constraint, ok := lo.Find(serviceObj.GetConfigConstraint(), func(constraint config.IConstraint) bool {
		return constraint.GetKey() == key
	})
	if ok {
		if err := constraint.Set(r.ActionHandler, val); err != nil {
			return false, fmt.Errorf("failed to update service config, service ID %s, namespace %s, key %s: %w", serviceID, constraint.GetNamespace(), key, err)
		}
		return true, nil
	}

	return false, fmt.Errorf("key %s not found in service ID %s", key, serviceID)
}

// GetCurrentTime is the resolver for the getCurrentTime field.
func (r *queryResolver) GetCurrentTime(ctx context.Context) (int64, error) {
	return r.TimeSource.Now().UnixMilli(), nil
}

// GetStationConfig is the resolver for the getStationConfig field.
func (r *queryResolver) GetStationConfig(ctx context.Context) (map[string]any, error) {
	config := make(map[string]any)
	for _, constraint := range r.StationConfigConstraints {
		namespace := constraint.GetNamespace()
		key := constraint.GetKey()
		val, err := constraint.Get(r.ActionHandler)
		if err != nil {
			return nil, fmt.Errorf("failed to get station config, namespace %s, key %s: %w", namespace, key, err)
		}
		config[key] = val
	}
	return config, nil
}

// GetSystemStatus is the resolver for the getSystemStatus field.
func (r *queryResolver) GetSystemStatus(ctx context.Context) (*graph_model.SystemStatus, error) {
	cpuPercent, err := system.GetCpuPercent()
	if err != nil {
		return nil, fmt.Errorf("failed to get CPU usage percent: %w", err)
	}

	memUsage, err := system.GetMemoryPercent()
	if err != nil {
		return nil, fmt.Errorf("failed to get memory usage: %w", err)
	}

	diskUsage, err := system.GetDiskPercent()
	if err != nil {
		return nil, fmt.Errorf("failed to get disk usage: %w", err)
	}

	uptime, err := system.GetOsUptime()
	if err != nil {
		return nil, fmt.Errorf("failed to get system uptime: %w", err)
	}

	return &graph_model.SystemStatus{
		CPU:    cpuPercent,
		Memory: memUsage,
		Disk:   diskUsage,
		Uptime: uptime,
	}, nil
}

// GetDeviceID is the resolver for the getDeviceId field.
func (r *queryResolver) GetDeviceID(ctx context.Context) (string, error) {
	if !r.checkIsAdmin(ctx) {
		return "********", nil
	}
	return r.HardwareDev.GetDeviceId(), nil
}

// GetDeviceConfig is the resolver for the getDeviceConfig field.
func (r *queryResolver) GetDeviceConfig(ctx context.Context) (*graph_model.DeviceConfig, error) {
	cfg := r.HardwareDev.GetConfig()
	return &graph_model.DeviceConfig{
		PacketInterval: cfg.GetPacketInterval().Milliseconds(),
		SampleRate:     cfg.GetSampleRate(),
		ChannelCodes:   cfg.GetChannelCodes(),
		GnssEnabled:    cfg.GetGnssAvailability(),
		Model:          cfg.GetModel(),
		Protocol:       cfg.GetProtocol(),
	}, nil
}

// GetDeviceStatus is the resolver for the getDeviceStatus field.
func (r *queryResolver) GetDeviceStatus(ctx context.Context) (*graph_model.DeviceStatus, error) {
	status := r.HardwareDev.GetStatus()
	return &graph_model.DeviceStatus{
		StartedAt: status.GetStartedAt().UnixMilli(),
		UpdatedAt: status.GetUpdatedAt().UnixMilli(),
		Frames:    status.GetFrames(),
		Errors:    status.GetErrors(),
		Messages:  status.GetMessages(),
	}, nil
}

// GetDeviceInfo is the resolver for the getDeviceInfo field.
func (r *queryResolver) GetDeviceInfo(ctx context.Context) (*graph_model.DeviceInfo, error) {
	var deviceInfo graph_model.DeviceInfo

	if temp, err := r.HardwareDev.GetTemperature(); err == nil {
		deviceInfo.Temperature = &temp
	}
	if lat, lon, elv, err := r.HardwareDev.GetCoordinates(!r.checkIsAdmin(ctx)); err == nil {
		deviceInfo.Latitude = &lat
		deviceInfo.Longitude = &lon
		deviceInfo.Elevation = &elv
	}

	return &deviceInfo, nil
}

// GetStationMetadata is the resolver for the getStationMetadata field.
func (r *queryResolver) GetStationMetadata(ctx context.Context, format string) (string, error) {
	stationAffiliationConfig := &config.StationAffiliationConfigConstraintImpl{}
	stationAffiliation, err := stationAffiliationConfig.Get(r.ActionHandler)
	if err != nil {
		return "", err
	}

	stationDescriptionConfig := &config.StationDescriptionConfigConstraintImpl{}
	stationDescription, err := stationDescriptionConfig.Get(r.ActionHandler)
	if err != nil {
		return "", err
	}

	stationCountryConfig := &config.StationCountryConfigConstraintImpl{}
	stationCountry, err := stationCountryConfig.Get(r.ActionHandler)
	if err != nil {
		return "", err
	}

	stationPlaceConfig := &config.StationPlaceConfigConstraintImpl{}
	stationPlace, err := stationPlaceConfig.Get(r.ActionHandler)
	if err != nil {
		return "", err
	}

	stationCodeConfig := &config.StationStationCodeConfigConstraintImpl{}
	stationCode, err := stationCodeConfig.Get(r.ActionHandler)
	if err != nil {
		return "", err
	}

	networkCodeConfig := &config.StationNetworkCodeConfigConstraintImpl{}
	networkCode, err := networkCodeConfig.Get(r.ActionHandler)
	if err != nil {
		return "", err
	}

	locationCodeConfig := &config.StationLocationCodeConfigConstraintImpl{}
	locationCode, err := locationCodeConfig.Get(r.ActionHandler)
	if err != nil {
		return "", err
	}

	// Ignore errors for non-AnyShake devices
	metadata, err := r.HardwareDev.GetMetadata(
		stationAffiliation.(string), stationDescription.(string), stationCountry.(string), stationPlace.(string),
		networkCode.(string), stationCode.(string), locationCode.(string), !r.checkIsAdmin(ctx),
	)
	if err != nil {
		return fmt.Sprintf("failed to get metadata: %s", err.Error()), nil
	}

	switch format {
	case "seiscomp_xml":
		return metadata.SeisComP(), nil
	case "station_xml":
		return metadata.StationXML(), nil
	}

	return "", fmt.Errorf("unknown format: %s", format)
}

// GetServiceConfigConstraint is the resolver for the getServiceConfigConstraint field.
func (r *queryResolver) GetServiceConfigConstraint(ctx context.Context) ([]*graph_model.ServiceConfigConstraint, error) {
	if !r.checkIsAdmin(ctx) {
		return nil, errors.New("permission denied")
	}

	var constraint []*graph_model.ServiceConfigConstraint
	for serviceId, serviceObj := range r.ServiceMap {
		constraintObj := serviceObj.GetConfigConstraint()
		serviceName := serviceObj.GetName()

		item := &graph_model.ServiceConfigConstraint{ServiceName: serviceName, ServiceID: serviceId}
		for _, con := range constraintObj {
			namespace, key := con.GetNamespace(), con.GetKey()
			currentVal, err := con.Get(r.ActionHandler)
			if err != nil {
				return nil, fmt.Errorf("failed to read current value for service namespace %s, key %s: %w", namespace, key, err)
			}
			item.Constraints = append(item.Constraints, &graph_model.ConfigConstraint{
				Namespace:    namespace,
				Key:          key,
				Name:         con.GetName(),
				Description:  con.GetDescription(),
				ConfigType:   string(con.GetType()),
				IsRequired:   con.IsRequired(),
				CurrentValue: currentVal,
				Options:      con.GetOptions(),
			})
		}

		constraint = append(constraint, item)
	}

	return constraint, nil
}

// GetStationConfigConstraint is the resolver for the getStationConfigConstraint field.
func (r *queryResolver) GetStationConfigConstraint(ctx context.Context) ([]*graph_model.ConfigConstraint, error) {
	if !r.checkIsAdmin(ctx) {
		return nil, errors.New("permission denied")
	}

	var constraint []*graph_model.ConfigConstraint
	for _, con := range r.StationConfigConstraints {
		namespace, key := con.GetNamespace(), con.GetKey()
		currentVal, err := con.Get(r.ActionHandler)
		if err != nil {
			return nil, fmt.Errorf("failed to read current value for namespace %s, key %s: %w", namespace, key, err)
		}
		constraint = append(constraint, &graph_model.ConfigConstraint{
			Namespace:    namespace,
			Key:          key,
			Name:         con.GetName(),
			Description:  con.GetDescription(),
			ConfigType:   string(con.GetType()),
			IsRequired:   con.IsRequired(),
			CurrentValue: currentVal,
			Options:      con.GetOptions(),
		})
	}

	return constraint, nil
}

// GetServiceStatus is the resolver for the getServiceStatus field.
func (r *queryResolver) GetServiceStatus(ctx context.Context) ([]*graph_model.ServiceStatus, error) {
	status := lo.MapToSlice(r.ServiceMap, func(serviceId string, srv service.IService) *graph_model.ServiceStatus {
		srvStatus := srv.GetStatus()
		return &graph_model.ServiceStatus{
			ServiceID:   serviceId,
			Name:        srv.GetName(),
			Description: srv.GetDescription(),
			Restarts:    srvStatus.GetRestarts(),
			IsRunning:   srvStatus.GetIsRunning(),
			StartedAt:   srvStatus.GetStartedAt().UnixMilli(),
			UpdatedAt:   srvStatus.GetUpdatedAt().UnixMilli(),
			StoppedAt:   srvStatus.GetStoppedAt().UnixMilli(),
		}
	})

	return status, nil
}

// GetEventSource is the resolver for the getEventSource field.
func (r *queryResolver) GetEventSource(ctx context.Context) ([]*graph_model.SeisEventSource, error) {
	sources := lo.MapToSlice(r.SeisEventSource, func(code string, source seisevent.IDataSource) *graph_model.SeisEventSource {
		property := source.GetProperty()
		locales := lo.MapEntries(property.Locales, func(lang, val string) (string, any) { return lang, val })
		return &graph_model.SeisEventSource{
			Locales:       locales,
			ID:            property.ID,
			Country:       property.Country,
			DefaultLocale: property.Deafult,
		}
	})

	return sources, nil
}

// GetEventsBySource is the resolver for the getEventsBySource field.
func (r *queryResolver) GetEventsBySource(ctx context.Context, code string) ([]*graph_model.SeisEvent, error) {
	source, ok := r.SeisEventSource[code]
	if !ok {
		return nil, fmt.Errorf("seismic event data source not found: %s", code)
	}

	lat, lon, _, err := r.HardwareDev.GetCoordinates(r.checkIsAdmin(ctx))
	if err != nil {
		return nil, fmt.Errorf("failed to get current coordinates of device: %w", err)
	}

	events, err := source.GetEvents(lat, lon)
	if err != nil {
		return nil, err
	}

	var seisEvents []*graph_model.SeisEvent
	for _, event := range events {
		mag := make(map[string]any, len(event.Magnitude))
		for _, m := range event.Magnitude {
			mag[string(m.Type)] = m.Value
		}
		seisEvents = append(seisEvents, &graph_model.SeisEvent{
			Verfied:   event.Verfied,
			Timestamp: event.Timestamp,
			EventID:   event.Event,
			Region:    event.Region,
			Depth:     event.Depth,
			Latitude:  event.Latitude,
			Longitude: event.Longitude,
			Magnitude: mag,
			Distance:  event.Distance,
			Estimation: []float64{
				event.Estimation.P_Wave,
				event.Estimation.S_Wave,
			},
		})
	}

	return seisEvents, nil
}

// GetSeisRecordsByTime is the resolver for the getSeisRecordsByTime field.
func (r *queryResolver) GetSeisRecordsByTime(ctx context.Context, startTime int64, endTime int64) ([]*graph_model.SeisRecord, error) {
	if endTime-startTime > time.Hour.Milliseconds() {
		return nil, fmt.Errorf("duration between start time and end time exceeds 1 hour limit")
	}

	records, err := r.ActionHandler.SeisRecordsQuery(time.UnixMilli(startTime), time.UnixMilli(endTime))
	if err != nil {
		return nil, fmt.Errorf("failed to query seismic waveform records: %w", err)
	}

	var respRecords []*graph_model.SeisRecord
	for _, record := range records {
		t, sampleRate, channelData, err := record.Decode()
		if err != nil {
			return nil, fmt.Errorf("failed to decode seismic waveform record: %w", err)
		}
		respChannelData := make([]*graph_model.ChannelData, len(channelData))
		for i, data := range channelData {
			respChannelData[i] = &graph_model.ChannelData{
				ChannelCode: data.ChannelCode,
				ChannelID:   data.ChannelId,
				Data:        data.Data,
			}
		}
		respRecords = append(respRecords, &graph_model.SeisRecord{
			Timestamp:   t.UnixMilli(),
			SampleRate:  sampleRate,
			ChannelData: respChannelData,
		})
	}

	return respRecords, nil
}

// GetMiniSeedFiles is the resolver for the getMiniSeedFiles field.
func (r *queryResolver) GetMiniSeedFiles(ctx context.Context) ([]*graph_model.ServiceAsset, error) {
	serviceObj, ok := r.ServiceMap[service_miniseed.ID]
	if !ok {
		return nil, fmt.Errorf("service was not found, maybe it was excluded from building")
	}
	assets, err := serviceObj.GetAssetList()
	if err != nil {
		return nil, fmt.Errorf("failed to get assets list for MiniSEED service: %w", err)
	}

	var files []*graph_model.ServiceAsset
	for _, asset := range assets {
		files = append(files, &graph_model.ServiceAsset{
			Namespace:  service_miniseed.ID,
			FilePath:   asset.FilePath,
			Size:       asset.Size,
			FileName:   asset.FileName,
			ModifiedAt: asset.ModifiedAt,
		})
	}
	return files, nil
}

// GetHelicorderFiles is the resolver for the getHelicorderFiles field.
func (r *queryResolver) GetHelicorderFiles(ctx context.Context) ([]*graph_model.ServiceAsset, error) {
	serviceObj, ok := r.ServiceMap[service_helicorder.ID]
	if !ok {
		return nil, fmt.Errorf("service was not found, maybe it was excluded from building")
	}
	assets, err := serviceObj.GetAssetList()
	if err != nil {
		return nil, fmt.Errorf("failed to get assets list for Helicorder service: %w", err)
	}

	var files []*graph_model.ServiceAsset
	for _, asset := range assets {
		files = append(files, &graph_model.ServiceAsset{
			Namespace:  service_helicorder.ID,
			FilePath:   asset.FilePath,
			Size:       asset.Size,
			FileName:   asset.FileName,
			ModifiedAt: asset.ModifiedAt,
		})
	}
	return files, nil
}

// GetCurrentUser is the resolver for the getCurrentUser field.
func (r *queryResolver) GetCurrentUser(ctx context.Context) (*graph_model.SysUser, error) {
	currentUserId := r.getCurrentUserId(ctx)
	sysUser, err := r.ActionHandler.SysUserGetByUserId(currentUserId)
	if err != nil {
		return nil, fmt.Errorf("failed to get current user info: %w", err)
	}
	return &graph_model.SysUser{
		CreatedAt: sysUser.CreatedAt,
		UserID:    sysUser.UserId,
		Username:  sysUser.Username,
		LastLogin: sysUser.LastLogin,
		UserIP:    sysUser.UserIp,
		UserAgent: sysUser.UserAgent,
		Admin:     sysUser.IsAdmin == model.ADMIN,
		UpdatedAt: sysUser.UpdatedAt,
	}, nil
}

// GetSysUsers is the resolver for the getSysUsers field.
func (r *queryResolver) GetSysUsers(ctx context.Context) ([]*graph_model.SysUser, error) {
	if !r.checkIsAdmin(ctx) {
		return nil, errors.New("permission denied")
	}

	users, err := r.ActionHandler.SysUserList()
	if err != nil {
		return nil, fmt.Errorf("failed to get system users: %w", err)
	}

	var sysUsers []*graph_model.SysUser
	for _, user := range users {
		sysUsers = append(sysUsers, &graph_model.SysUser{
			CreatedAt: user.CreatedAt,
			UserID:    user.UserId,
			Username:  user.Username,
			LastLogin: user.LastLogin,
			UserIP:    user.UserIp,
			UserAgent: user.UserAgent,
			Admin:     user.IsAdmin == model.ADMIN,
			UpdatedAt: user.UpdatedAt,
		})
	}

	return sysUsers, nil
}

// IsGenuineProduct is the resolver for the isGenuineProduct field.
func (r *queryResolver) IsGenuineProduct(ctx context.Context) (bool, error) {
	if !r.checkIsAdmin(ctx) {
		return false, errors.New("permission denied")
	}

	// Protocol v1 is open for everyone
	deviceConfig := r.HardwareDev.GetConfig()
	if protocol := deviceConfig.GetProtocol(); protocol == "v1" {
		return false, nil
	}

	currentDeviceId := r.HardwareDev.GetDeviceId()
	return !lo.Contains([]string{"FFFFFFFF", "00000000"}, currentDeviceId), nil
}

// GetApplicationLogs is the resolver for the getApplicationLogs field.
func (r *queryResolver) GetApplicationLogs(ctx context.Context) ([]string, error) {
	if !r.checkIsAdmin(ctx) {
		return nil, errors.New("permission denied")
	}

	return r.LogBuffer.Values(), nil
}

// ExportGlobalConfig is the resolver for the exportGlobalConfig field.
func (r *queryResolver) ExportGlobalConfig(ctx context.Context) (string, error) {
	if !r.checkIsAdmin(ctx) {
		return "", errors.New("permission denied")
	}

	/*
		{
			"namespace": {
				"key": {
					"value": "value",
					"version": 1,
					"type": "string"
				}
			}
		}
	*/
	globalConfig := make(map[string]map[string]map[string]any)

	for _, constraint := range r.StationConfigConstraints {
		namespace := constraint.GetNamespace()
		val, err := constraint.Get(r.ActionHandler)
		if err != nil {
			return "", err
		}
		if globalConfig[namespace] == nil {
			globalConfig[namespace] = make(map[string]map[string]any)
		}
		globalConfig[namespace][constraint.GetKey()] = map[string]any{
			"version": constraint.GetVersion(),
			"value":   val,
			"type":    string(constraint.GetType()),
		}
	}

	for _, serviceObj := range r.ServiceMap {
		for _, constraint := range serviceObj.GetConfigConstraint() {
			namespace := constraint.GetNamespace()
			val, err := constraint.Get(r.ActionHandler)
			if err != nil {
				return "", err
			}
			if globalConfig[namespace] == nil {
				globalConfig[namespace] = make(map[string]map[string]any)
			}
			globalConfig[namespace][constraint.GetKey()] = map[string]any{
				"version": constraint.GetVersion(),
				"value":   val,
				"type":    string(constraint.GetType()),
			}
		}
	}

	globalConfigJson, err := json.Marshal(globalConfig)
	if err != nil {
		return "", err
	}
	return string(globalConfigJson), nil
}

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

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

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