package main

import (
	"bufio"
	"context"
	"errors"
	"flag"
	"fmt"
	"log/slog"
	"net"
	"net/url"
	"os"
	"os/signal"
	"path/filepath"
	"runtime/debug"
	"strings"
	"sync"
	"syscall"
	"time"

	"gitea.lerkolabs.com/lerkolabs/uptop/internal/cluster"
	"gitea.lerkolabs.com/lerkolabs/uptop/internal/config"
	"gitea.lerkolabs.com/lerkolabs/uptop/internal/importer"
	"gitea.lerkolabs.com/lerkolabs/uptop/internal/models"
	"gitea.lerkolabs.com/lerkolabs/uptop/internal/monitor"
	"gitea.lerkolabs.com/lerkolabs/uptop/internal/server"
	"gitea.lerkolabs.com/lerkolabs/uptop/internal/store"
	"gitea.lerkolabs.com/lerkolabs/uptop/internal/tui"

	tea "github.com/charmbracelet/bubbletea"
	"github.com/charmbracelet/ssh"
	"github.com/charmbracelet/wish"
	bm "github.com/charmbracelet/wish/bubbletea"
	"github.com/mattn/go-isatty"
)

var (
	version = "dev"
	commit  = "none"
	date    = "unknown"
)

// GoReleaser stamps the vars above via ldflags, but `go install module@tag`
// compiles without them and would report "dev". The module version and any
// vcs stamps are embedded in every binary, so fall back to those.
func init() {
	if version != "dev" {
		return
	}
	info, ok := debug.ReadBuildInfo()
	if !ok {
		return
	}
	if mv := info.Main.Version; mv != "" && mv != "(devel)" {
		mv = strings.TrimPrefix(mv, "v")
		// Pseudo-versions (e.g. "0.1.1-0.20260620165311-5ca534b0b100+dirty")
		// are noisy in the TUI footer. Extract just the base semver.
		if i := strings.Index(mv, "-0."); i > 0 {
			mv = mv[:i]
		}
		mv = strings.TrimSuffix(mv, "+dirty")
		version = mv
	}
	for _, s := range info.Settings {
		switch s.Key {
		case "vcs.revision":
			commit = s.Value
		case "vcs.time":
			date = s.Value
		}
	}
}

func main() {
	slog.SetDefault(slog.New(slog.NewTextHandler(os.Stderr, &slog.HandlerOptions{
		Level: slog.LevelInfo,
	})))

	if len(os.Args) >= 2 {
		switch os.Args[1] {
		case "apply":
			runApply(os.Args[2:])
			return
		case "export":
			runExport(os.Args[2:])
			return
		case "version", "--version", "-v", "-version":
			printVersion()
			return
		case "migrate-secrets":
			runMigrateSecrets(os.Args[2:])
			return
		case "help", "--help", "-h":
			printUsage()
			return
		case "serve":
			runServe(os.Args[2:])
			return
		}
	}
	runServe(os.Args[1:])
}

func printUsage() {
	fmt.Fprintf(os.Stderr, `Usage: uptop <command> [flags]

Commands:
  serve             Start the server (default if no command given)
  apply             Apply monitors from a YAML file
  export            Export monitors to YAML
  migrate-secrets   Re-encrypt alert credentials with current key
  version           Print version and exit

Run 'uptop serve --help' for server flags.
`)
}

func printVersion() {
	out := "uptop " + version
	var meta []string
	if commit != "none" {
		meta = append(meta, commit)
	}
	if date != "unknown" {
		meta = append(meta, date)
	}
	if len(meta) > 0 {
		out += " (" + strings.Join(meta, ", ") + ")"
	}
	fmt.Println(out)
}

func envOrDefault(key, fallback string) string {
	if v := os.Getenv(key); v != "" {
		return v
	}
	return fallback
}

func redactDSN(dsn string) string {
	u, err := url.Parse(dsn)
	if err != nil {
		return "***"
	}
	u.User = nil
	return u.String()
}

// parseTrustedProxies turns UPTOP_TRUSTED_PROXIES (comma-separated CIDRs or
// bare IPs) into networks the rate limiter trusts to set X-Forwarded-For. Bare
// IPs are treated as single-host ranges. Invalid entries are warned about and
// skipped, so a typo degrades to "ignore XFF" (safe) rather than aborting boot.
func parseTrustedProxies(raw string) []*net.IPNet {
	if strings.TrimSpace(raw) == "" {
		return nil
	}
	var cidrs []*net.IPNet
	for _, part := range strings.Split(raw, ",") {
		part = strings.TrimSpace(part)
		if part == "" {
			continue
		}
		if !strings.Contains(part, "/") {
			if ip := net.ParseIP(part); ip != nil {
				bits := 32
				if ip.To4() == nil {
					bits = 128
				}
				part = fmt.Sprintf("%s/%d", part, bits)
			}
		}
		_, ipnet, err := net.ParseCIDR(part)
		if err != nil {
			slog.Warn("ignoring invalid UPTOP_TRUSTED_PROXIES entry", "entry", part, "err", err) //nolint:gosec // structured slog, not format string
			continue
		}
		cidrs = append(cidrs, ipnet)
	}
	return cidrs
}

func openStore(ctx context.Context, dbType, dsn string) store.Store {
	var ss *store.SQLStore
	var err error
	if dbType == "postgres" {
		ss, err = store.NewPostgresStore(dsn)
	} else {
		ss, err = store.NewSQLiteStore(dsn)
	}
	if err != nil {
		slog.Error("database connection failed", "err", err)
		os.Exit(1)
	}
	if encKey := os.Getenv("UPTOP_ENCRYPTION_KEY"); encKey != "" {
		enc, err := store.NewEncryptor(encKey)
		if err != nil {
			slog.Error("encryption key invalid", "err", err)
			os.Exit(1)
		}
		ss.SetEncryptor(enc)
	} else {
		slog.Warn("no UPTOP_ENCRYPTION_KEY set, alert credentials stored unencrypted")
	}
	if err := ss.Init(ctx); err != nil {
		slog.Error("database init failed", "err", err)
		os.Exit(1)
	}
	return ss
}

func runApply(args []string) {
	fs := flag.NewFlagSet("apply", flag.ExitOnError)
	filePath := fs.String("f", "", "Path to YAML config file (required)")
	dryRun := fs.Bool("dry-run", false, "Show planned changes without applying")
	prune := fs.Bool("prune", false, "Delete monitors/alerts not in YAML")
	dbType := fs.String("db-type", envOrDefault("UPTOP_DB_TYPE", "sqlite"), "Database type (sqlite or postgres)")
	dsn := fs.String("dsn", envOrDefault("UPTOP_DB_DSN", "uptop.db"), "Database DSN")
	_ = fs.Parse(args) // ExitOnError: parse errors exit before returning

	if *filePath == "" {
		fmt.Fprintln(os.Stderr, "error: -f flag is required")
		fs.Usage()
		os.Exit(1)
	}

	ctx := context.Background()
	s := openStore(ctx, *dbType, *dsn)

	f, err := config.LoadFile(*filePath)
	if err != nil {
		slog.Error("config load failed", "err", err)
		os.Exit(1)
	}

	changes, err := config.Apply(ctx, s, f, config.ApplyOpts{
		DryRun: *dryRun,
		Prune:  *prune,
	})
	if err != nil {
		slog.Error("config apply failed", "err", err)
		os.Exit(1)
	}

	fmt.Print(config.FormatChanges(changes, *dryRun))
}

func runExport(args []string) {
	fs := flag.NewFlagSet("export", flag.ExitOnError)
	outPath := fs.String("o", "-", "Output file path (- for stdout)")
	dbType := fs.String("db-type", envOrDefault("UPTOP_DB_TYPE", "sqlite"), "Database type (sqlite or postgres)")
	dsn := fs.String("dsn", envOrDefault("UPTOP_DB_DSN", "uptop.db"), "Database DSN")
	_ = fs.Parse(args) // ExitOnError: parse errors exit before returning

	ctx := context.Background()
	s := openStore(ctx, *dbType, *dsn)

	f, err := config.Export(ctx, s)
	if err != nil {
		slog.Error("export failed", "err", err)
		os.Exit(1)
	}

	if err := config.WriteFile(f, *outPath); err != nil {
		slog.Error("export write failed", "err", err)
		os.Exit(1)
	}
}

func runMigrateSecrets(args []string) {
	fs := flag.NewFlagSet("migrate-secrets", flag.ExitOnError)
	dbType := fs.String("db-type", envOrDefault("UPTOP_DB_TYPE", "sqlite"), "Database type (sqlite or postgres)")
	dsn := fs.String("dsn", envOrDefault("UPTOP_DB_DSN", "uptop.db"), "Database DSN")
	_ = fs.Parse(args)

	encKey := os.Getenv("UPTOP_ENCRYPTION_KEY")
	if encKey == "" {
		fmt.Fprintln(os.Stderr, "error: UPTOP_ENCRYPTION_KEY must be set")
		os.Exit(1)
	}
	enc, err := store.NewEncryptor(encKey)
	if err != nil {
		slog.Error("encryption key invalid", "err", err)
		os.Exit(1)
	}

	ctx := context.Background()

	var ss *store.SQLStore
	if *dbType == "postgres" {
		ss, err = store.NewPostgresStore(*dsn)
	} else {
		ss, err = store.NewSQLiteStore(*dsn)
	}
	if err != nil {
		slog.Error("database connection failed", "err", err)
		os.Exit(1)
	}
	if err := ss.Init(ctx); err != nil {
		slog.Error("database init failed", "err", err)
		os.Exit(1)
	}

	ss.SetEncryptor(enc)

	alerts, err := ss.GetAllAlerts(ctx)
	if err != nil {
		slog.Error("failed to load alerts", "err", err)
		os.Exit(1)
	}
	migrated := 0
	for _, a := range alerts {
		if err := ss.UpdateAlert(ctx, a.ID, a.Name, a.Type, a.Settings); err != nil {
			slog.Error("alert migration failed", "alert", a.Name, "err", err)
			os.Exit(1)
		}
		migrated++
	}
	fmt.Printf("Migrated %d alert(s) to encrypted storage.\n", migrated)
}

func runServe(args []string) {
	cfg := parseConfig()

	if cfg.ClusterMode == "probe" {
		if cfg.NodeID == "" {
			fmt.Fprintln(os.Stderr, "UPTOP_NODE_ID is required for probe mode")
			os.Exit(1)
		}
		if cfg.PeerURL == "" {
			fmt.Fprintln(os.Stderr, "UPTOP_PEER_URL is required for probe mode")
			os.Exit(1)
		}

		fmt.Printf("Cluster: Running as PROBE (node=%s, region=%s)\n", cfg.NodeID, cfg.NodeRegion)

		ctx, cancel := context.WithCancel(context.Background())
		defer cancel()
		done := make(chan os.Signal, 1)
		signal.Notify(done, os.Interrupt, syscall.SIGINT, syscall.SIGTERM)
		go func() {
			<-done
			cancel()
		}()

		if cfg.AllowPrivateTargets {
			slog.Warn("private target blocking disabled, monitor URLs can reach internal networks")
		}

		if err := cluster.RunProbe(ctx, cluster.ProbeConfig{
			NodeID:              cfg.NodeID,
			NodeName:            cfg.NodeName,
			Region:              cfg.NodeRegion,
			LeaderURL:           cfg.PeerURL,
			SharedKey:           cfg.ClusterSecret,
			Interval:            30,
			AllowPrivateTargets: cfg.AllowPrivateTargets,
		}); err != nil {
			slog.Error("probe failed", "err", err)
		}
		return
	}

	fs := flag.NewFlagSet("serve", flag.ExitOnError)
	port := fs.Int("port", cfg.Port, "SSH Port")
	flagDBType := fs.String("db-type", cfg.DBType, "Database type (sqlite or postgres)")
	flagDSN := fs.String("dsn", cfg.DBDSN, "Database DSN")
	demo := fs.Bool("demo", false, "Seed demo data")
	importKuma := fs.String("import-kuma", "", "Import Uptime Kuma backup JSON file")
	_ = fs.Parse(args) // ExitOnError: parse errors exit before returning

	var ss *store.SQLStore
	var dbErr error
	if *flagDBType == "postgres" {
		ss, dbErr = store.NewPostgresStore(*flagDSN)
		slog.Info("database connected", "type", "postgres", "dsn", redactDSN(*flagDSN))
	} else {
		ss, dbErr = store.NewSQLiteStore(*flagDSN)
		slog.Info("database connected", "type", "sqlite", "dsn", *flagDSN)
	}
	if dbErr != nil {
		slog.Error("database connection failed", "err", dbErr)
		os.Exit(1)
	}
	defer ss.Close()

	if cfg.EncryptionKey != "" {
		enc, err := store.NewEncryptor(cfg.EncryptionKey)
		if err != nil {
			slog.Error("encryption key invalid", "err", err)
			os.Exit(1)
		}
		ss.SetEncryptor(enc)
	} else {
		slog.Warn("no UPTOP_ENCRYPTION_KEY set, alert credentials stored unencrypted")
	}

	kc := newKeyCache(ss)
	var s store.Store = &userInvalidatingStore{Store: ss, kc: kc}

	ctx, cancel := context.WithCancel(context.Background())
	defer cancel()

	if err := s.Init(ctx); err != nil {
		slog.Error("database init failed", "err", err)
		os.Exit(1)
	}
	if *demo {
		seedDemoData(ctx, s)
	}

	seedKeysFromEnv(ctx, s)

	if *importKuma != "" {
		kb, err := importer.LoadKumaFile(*importKuma)
		if err != nil {
			slog.Error("kuma import failed", "err", err)
			os.Exit(1)
		}
		backup := importer.ConvertKuma(kb)
		if err := s.ImportData(ctx, backup); err != nil {
			slog.Error("import failed", "err", err)
			os.Exit(1)
		}
		fmt.Printf("Imported %d monitors and %d alerts from Uptime Kuma v%s\n", len(backup.Sites), len(backup.Alerts), kb.Version)
	}

	if cfg.AllowPrivateTargets {
		slog.Warn("private target blocking disabled, monitor URLs can reach internal networks")
	}

	eng := monitor.NewEngineWithOpts(s, cfg.AllowPrivateTargets)
	if cfg.InsecureSkipVerify {
		eng.SetInsecureSkipVerify(true)
	}
	if cfg.AggStrategy != "" {
		eng.SetAggStrategy(monitor.AggregationStrategy(cfg.AggStrategy))
	}
	eng.SetMaintRetention(cfg.MaintRetention)

	eng.InitHistory(ctx)
	eng.InitLogs(ctx)
	eng.InitAlertHealth(ctx)
	eng.Start(ctx)

	localTUI := isatty.IsTerminal(os.Stdout.Fd()) || isatty.IsCygwinTerminal(os.Stdout.Fd())

	httpSrv := server.Start(cfg.serverConfig(localTUI), s, eng)

	cluster.Start(ctx, cluster.Config{
		Mode:      cfg.ClusterMode,
		PeerURL:   cfg.PeerURL,
		SharedKey: cfg.ClusterSecret,
	}, eng)

	sshSrv := startSSHServer(*port, s, eng, kc)

	if localTUI {
		p := tea.NewProgram(tui.InitialModel(ctx, true, s, eng, version), tea.WithAltScreen(), tea.WithMouseCellMotion())
		if _, err := p.Run(); err != nil {
			slog.Error("TUI failed", "err", err)
		}
	} else {
		fmt.Println("uptop running in HEADLESS mode")
		done := make(chan os.Signal, 1)
		signal.Notify(done, os.Interrupt, syscall.SIGINT, syscall.SIGTERM)
		<-done
		fmt.Println("Shutting down...")
	}
	cancel()

	eng.Stop()

	shutdownCtx, shutdownCancel := context.WithTimeout(context.Background(), 30*time.Second)
	defer shutdownCancel()
	if httpSrv != nil {
		if err := httpSrv.Shutdown(shutdownCtx); err != nil {
			slog.Error("HTTP shutdown failed", "err", err)
		}
	}
	if sshSrv != nil {
		if err := sshSrv.Shutdown(shutdownCtx); err != nil {
			slog.Error("SSH shutdown failed", "err", err)
		}
	}
}

func startSSHServer(port int, db store.Store, eng *monitor.Engine, kc *keyCache) *ssh.Server {
	s, err := wish.NewServer(
		wish.WithAddress(fmt.Sprintf(":%d", port)),
		wish.WithHostKeyPath(envOrDefault("UPTOP_SSH_HOST_KEY", ".ssh/id_ed25519")),
		wish.WithPublicKeyAuth(func(ctx ssh.Context, key ssh.PublicKey) bool {
			return kc.IsAllowed(ctx, key)
		}),
		wish.WithMiddleware(
			bm.Middleware(func(s ssh.Session) (tea.Model, []tea.ProgramOption) {
				return tui.InitialModel(s.Context(), false, db, eng, version), []tea.ProgramOption{tea.WithAltScreen(), tea.WithMouseCellMotion()}
			}),
		),
	)
	if err != nil {
		slog.Error("SSH server failed", "err", err)
		return nil
	}
	go func() {
		if err := s.ListenAndServe(); err != nil && !errors.Is(err, ssh.ErrServerClosed) {
			slog.Error("SSH server failed", "err", err)
		}
	}()
	return s
}

func seedDemoData(ctx context.Context, s store.Store) {
	existing, _ := s.GetSites(ctx)
	if len(existing) > 0 {
		return
	}
	fmt.Println("Seeding demo data...")

	if err := s.AddAlert(ctx, "Discord Ops", "discord", map[string]string{"url": "https://discord.com/api/webhooks/demo/token"}); err != nil {
		slog.Error("demo seed failed", "step", "add alert", "err", err)
		return
	}
	if err := s.AddAlert(ctx, "Slack Infra", "slack", map[string]string{"url": "https://hooks.slack.com/services/DEMO/WEBHOOK"}); err != nil {
		slog.Error("demo seed failed", "step", "add alert", "err", err)
		return
	}
	if err := s.AddAlert(ctx, "Email Oncall", "email", map[string]string{
		"host": "smtp.example.com", "port": "587",
		"user": "oncall@example.com", "pass": "replace-me",
		"from": "oncall@example.com", "to": "team@example.com",
	}); err != nil {
		slog.Error("demo seed failed", "step", "add alert", "err", err)
		return
	}

	alerts, _ := s.GetAllAlerts(ctx)
	alertID := 0
	if len(alerts) > 0 {
		alertID = alerts[0].ID
	}

	demoSites := []models.SiteConfig{
		{Name: "Google", URL: "https://www.google.com", Type: "http", Interval: 30, AlertID: alertID, CheckSSL: true, ExpiryThreshold: 14, MaxRetries: 2},
		{Name: "GitHub", URL: "https://github.com", Type: "http", Interval: 30, AlertID: alertID, CheckSSL: true, ExpiryThreshold: 7, MaxRetries: 3},
		{Name: "Cloudflare DNS", URL: "https://1.1.1.1", Type: "http", Interval: 60, AlertID: alertID, ExpiryThreshold: 7, MaxRetries: 1},
		{Name: "JSON Placeholder", URL: "https://jsonplaceholder.typicode.com/posts/1", Type: "http", Interval: 45, AlertID: alertID, ExpiryThreshold: 7, MaxRetries: 2},
		{Name: "Nonexistent Site", URL: "https://this-domain-does-not-exist-12345.com", Type: "http", Interval: 30, AlertID: alertID, ExpiryThreshold: 7, MaxRetries: 3},
		{Name: "Bad Port", URL: "https://localhost:19999", Type: "http", Interval: 30, ExpiryThreshold: 7, MaxRetries: 1},
		{Name: "Backup Cron", Type: "push", Interval: 300, AlertID: alertID, ExpiryThreshold: 7},
		{Name: "DB Healthcheck", Type: "push", Interval: 120, AlertID: alertID, ExpiryThreshold: 7},
		{Name: "Gateway", Type: "ping", Interval: 30, AlertID: alertID, Hostname: "10.0.0.1", Timeout: 5, ExpiryThreshold: 7},
		{Name: "SSH Server", Type: "port", Interval: 60, AlertID: alertID, Hostname: "10.0.0.1", Port: 22, Timeout: 5, ExpiryThreshold: 7},
	}
	for _, site := range demoSites {
		if err := s.AddSite(ctx, site); err != nil {
			slog.Error("demo seed failed", "step", "add site", "site", site.Name, "err", err)
		}
	}
}

type keyCache struct {
	mu      sync.RWMutex
	keys    []ssh.PublicKey
	updated time.Time
	ttl     time.Duration
	db      store.Store
}

func newKeyCache(db store.Store) *keyCache {
	return &keyCache{db: db, ttl: 30 * time.Second}
}

func (c *keyCache) refresh(ctx context.Context) {
	users, err := c.db.GetAllUsers(ctx)
	if err != nil {
		// Keep the previous key set: a transient DB error must not lock every
		// admin out. Revocation still fails closed because Invalidate clears
		// the set immediately.
		slog.Error("SSH key cache refresh failed", "err", err)
		return
	}
	keys := make([]ssh.PublicKey, 0, len(users))
	for _, u := range users {
		k, _, _, _, err := ssh.ParseAuthorizedKey([]byte(u.PublicKey))
		if err != nil {
			continue
		}
		keys = append(keys, k)
	}
	c.mu.Lock()
	c.keys = keys
	c.updated = time.Now()
	c.mu.Unlock()
}

// Invalidate clears the cached key set, not just the timestamp. If the
// refresh that follows a user revocation fails, auth fails closed (everyone
// re-authenticates after the next successful refresh) instead of the revoked
// key silently continuing to work off the stale cache.
func (c *keyCache) Invalidate() {
	c.mu.Lock()
	c.keys = nil
	c.updated = time.Time{}
	c.mu.Unlock()
}

func (c *keyCache) IsAllowed(ctx context.Context, incomingKey ssh.PublicKey) bool {
	c.mu.RLock()
	stale := time.Since(c.updated) > c.ttl
	c.mu.RUnlock()

	if stale {
		c.refresh(ctx)
	}

	c.mu.RLock()
	defer c.mu.RUnlock()
	for _, k := range c.keys {
		if ssh.KeysEqual(k, incomingKey) {
			return true
		}
	}
	return false
}

// userInvalidatingStore drops the SSH key cache whenever the user table
// changes, so a revocation takes effect on the next connection attempt
// instead of after the cache TTL — and fails closed if the DB is unreachable
// when that next attempt re-reads the table.
type userInvalidatingStore struct {
	store.Store
	kc *keyCache
}

func (s *userInvalidatingStore) AddUser(ctx context.Context, username, publicKey, role string) error {
	err := s.Store.AddUser(ctx, username, publicKey, role)
	s.kc.Invalidate()
	return err
}

func (s *userInvalidatingStore) UpdateUser(ctx context.Context, id int, username, publicKey, role string) error {
	err := s.Store.UpdateUser(ctx, id, username, publicKey, role)
	s.kc.Invalidate()
	return err
}

func (s *userInvalidatingStore) DeleteUser(ctx context.Context, id int) error {
	err := s.Store.DeleteUser(ctx, id)
	s.kc.Invalidate()
	return err
}

func (s *userInvalidatingStore) ImportData(ctx context.Context, data models.Backup) error {
	err := s.Store.ImportData(ctx, data)
	s.kc.Invalidate()
	return err
}

func seedKeysFromEnv(ctx context.Context, s store.Store) {
	var keys []string

	if v := os.Getenv("UPTOP_ADMIN_KEY"); v != "" {
		keys = append(keys, strings.TrimSpace(v))
	}

	if path := os.Getenv("UPTOP_KEYS"); path != "" {
		f, err := os.Open(filepath.Clean(path))
		if err != nil {
			slog.Warn("failed to open UPTOP_KEYS file", "path", path, "err", err) //nolint:gosec // structured slog, not format string
		} else {
			scanner := bufio.NewScanner(f)
			for scanner.Scan() {
				line := strings.TrimSpace(scanner.Text())
				if line == "" || strings.HasPrefix(line, "#") {
					continue
				}
				keys = append(keys, line)
			}
			_ = f.Close()
		}
	}

	if len(keys) == 0 {
		return
	}

	existing, err := s.GetAllUsers(ctx)
	if err != nil {
		slog.Warn("could not check existing users", "err", err)
		return
	}

	existingKeys := make(map[string]bool)
	for _, u := range existing {
		existingKeys[u.PublicKey] = true
	}

	added := 0
	for i, key := range keys {
		if existingKeys[key] {
			continue
		}

		username := usernameFromKey(key, i, len(existing)+added)
		if err := s.AddUser(ctx, username, key, "admin"); err != nil {
			slog.Warn("failed to seed user", "user", username, "err", err) //nolint:gosec // structured slog, not format string
			continue
		}
		fmt.Printf("Seeded admin user %q from %s\n", username, seedSource(i, len(keys), os.Getenv("UPTOP_ADMIN_KEY") != ""))
		added++
	}
}

func usernameFromKey(key string, index, totalExisting int) string {
	parts := strings.Fields(key)
	if len(parts) >= 3 {
		comment := parts[2]
		if at := strings.Index(comment, "@"); at > 0 {
			return comment[:at]
		}
		return comment
	}
	if index == 0 && totalExisting == 0 {
		return "admin"
	}
	return fmt.Sprintf("user-%d", totalExisting+1)
}

func seedSource(index, total int, hasEnvKey bool) string {
	if hasEnvKey && index == 0 {
		return "UPTOP_ADMIN_KEY"
	}
	return "UPTOP_KEYS"
}
