package auth_test

import (
	"context"
	"io"
	"log/slog"
	"path/filepath"
	"runtime"
	"testing"
	"time"

	"update_server/internal/auth"
	"update_server/internal/config"
	"update_server/internal/db"
)

func TestEnsureBootstrapAdminCreatesSingleAdminUser(t *testing.T) {
	t.Parallel()

	ctx := context.Background()
	store := newTestStore(t)
	defer store.Close()

	cfg := config.Config{
		AdminEmail:        "admin@example.com",
		AdminPassword:     "correct horse battery staple",
		SessionCookieName: "update_server_session",
		SessionTTL:        24 * time.Hour,
	}

	logger := slog.New(slog.NewTextHandler(io.Discard, nil))
	service := auth.NewService(cfg, logger, store)

	if err := service.EnsureBootstrapAdmin(ctx); err != nil {
		t.Fatalf("bootstrap admin: %v", err)
	}

	if err := service.EnsureBootstrapAdmin(ctx); err != nil {
		t.Fatalf("bootstrap admin second pass: %v", err)
	}

	user, err := store.Users.GetByEmail(ctx, cfg.AdminEmail)
	if err != nil {
		t.Fatalf("load bootstrapped admin: %v", err)
	}

	if user.Role != db.UserRoleAdmin {
		t.Fatalf("expected admin role, got %q", user.Role)
	}

	if !user.IsActive {
		t.Fatal("expected bootstrapped admin to be active")
	}

	if user.PasswordHash == cfg.AdminPassword {
		t.Fatal("expected bootstrapped password to be hashed")
	}

	if err := auth.ComparePassword(user.PasswordHash, cfg.AdminPassword); err != nil {
		t.Fatalf("compare hashed password: %v", err)
	}

	var userCount int
	if err := store.DB.QueryRowContext(ctx, `SELECT COUNT(*) FROM users`).Scan(&userCount); err != nil {
		t.Fatalf("count users: %v", err)
	}

	if userCount != 1 {
		t.Fatalf("expected one bootstrapped user, got %d", userCount)
	}
}

func newTestStore(t *testing.T) *db.Store {
	t.Helper()

	sqlitePath := filepath.Join(t.TempDir(), "update-server.sqlite")
	database, err := db.Open(context.Background(), sqlitePath)
	if err != nil {
		t.Fatalf("open sqlite: %v", err)
	}

	if err := db.Migrate(context.Background(), database, testProjectPath(t, "migrations")); err != nil {
		database.Close()
		t.Fatalf("migrate sqlite: %v", err)
	}

	return db.NewStore(database)
}

func testProjectPath(t *testing.T, parts ...string) string {
	t.Helper()

	_, filename, _, ok := runtime.Caller(0)
	if !ok {
		t.Fatal("resolve caller path")
	}

	root := filepath.Join(filepath.Dir(filename), "..", "..")
	items := append([]string{root}, parts...)
	return filepath.Join(items...)
}
