package services

import (
	"log/slog"
	"path/filepath"
	"testing"
	"time"

	"warpbox.dev/backend/libs/config"
)

func TestSettingsLoadDefaultsAndOverrides(t *testing.T) {
	settings := newTestSettingsService(t)
	policy, err := settings.UploadPolicy()
	if err != nil {
		t.Fatalf("UploadPolicy returned error: %v", err)
	}
	if !policy.AnonymousUploadsEnabled || policy.AnonymousMaxUploadMB != 512 {
		t.Fatalf("default policy = %+v", policy)
	}

	policy.AnonymousUploadsEnabled = false
	policy.UserDailyUploadMB = 123
	if err := settings.UpdateUploadPolicy(policy); err != nil {
		t.Fatalf("UpdateUploadPolicy returned error: %v", err)
	}
	next, err := settings.UploadPolicy()
	if err != nil {
		t.Fatalf("UploadPolicy returned error: %v", err)
	}
	if next.AnonymousUploadsEnabled || next.UserDailyUploadMB != 123 {
		t.Fatalf("override policy = %+v", next)
	}
}

func TestSettingsUseNewEnvDefaultsUntilSaved(t *testing.T) {
	root := t.TempDir()
	upload, err := NewUploadService(1024*1024, filepath.Join(root, "data"), "http://example.test", slog.Default())
	if err != nil {
		t.Fatalf("NewUploadService returned error: %v", err)
	}
	defer upload.Close()

	first, err := NewSettingsService(upload.DB(), config.SettingsDefaults{
		AnonymousUploadsEnabled: true,
		AnonymousMaxUploadMB:    111,
		AnonymousDailyUploadMB:  222,
		UserDailyUploadMB:       333,
		DefaultUserStorageMB:    444,
		UsageRetentionDays:      30,
	})
	if err != nil {
		t.Fatalf("NewSettingsService first returned error: %v", err)
	}
	firstPolicy, err := first.UploadPolicy()
	if err != nil {
		t.Fatalf("UploadPolicy first returned error: %v", err)
	}
	if firstPolicy.AnonymousMaxUploadMB != 111 {
		t.Fatalf("first AnonymousMaxUploadMB = %v, want 111", firstPolicy.AnonymousMaxUploadMB)
	}

	second, err := NewSettingsService(upload.DB(), config.SettingsDefaults{
		AnonymousUploadsEnabled: true,
		AnonymousMaxUploadMB:    555,
		AnonymousDailyUploadMB:  666,
		UserDailyUploadMB:       777,
		DefaultUserStorageMB:    888,
		UsageRetentionDays:      30,
	})
	if err != nil {
		t.Fatalf("NewSettingsService second returned error: %v", err)
	}
	secondPolicy, err := second.UploadPolicy()
	if err != nil {
		t.Fatalf("UploadPolicy second returned error: %v", err)
	}
	if secondPolicy.AnonymousMaxUploadMB != 555 {
		t.Fatalf("second AnonymousMaxUploadMB = %v, want 555", secondPolicy.AnonymousMaxUploadMB)
	}

	if err := second.UpdateUploadPolicy(secondPolicy); err != nil {
		t.Fatalf("UpdateUploadPolicy returned error: %v", err)
	}
	third, err := NewSettingsService(upload.DB(), config.SettingsDefaults{
		AnonymousUploadsEnabled: true,
		AnonymousMaxUploadMB:    999,
		AnonymousDailyUploadMB:  999,
		UserDailyUploadMB:       999,
		DefaultUserStorageMB:    999,
		UsageRetentionDays:      30,
	})
	if err != nil {
		t.Fatalf("NewSettingsService third returned error: %v", err)
	}
	thirdPolicy, err := third.UploadPolicy()
	if err != nil {
		t.Fatalf("UploadPolicy third returned error: %v", err)
	}
	if thirdPolicy.AnonymousMaxUploadMB != 555 {
		t.Fatalf("third AnonymousMaxUploadMB = %v, want persisted 555", thirdPolicy.AnonymousMaxUploadMB)
	}
}

func TestSettingsRejectInvalidMegabytes(t *testing.T) {
	if _, err := ParseMegabytesValue("0"); err == nil {
		t.Fatalf("ParseMegabytesValue accepted zero")
	}
	settings := newTestSettingsService(t)
	policy, err := settings.UploadPolicy()
	if err != nil {
		t.Fatalf("UploadPolicy returned error: %v", err)
	}
	policy.DefaultUserStorageMB = -1
	if err := settings.UpdateUploadPolicy(policy); err == nil {
		t.Fatalf("UpdateUploadPolicy accepted negative storage")
	}
}

func TestUploadPolicyAllowsNegativeOneForUnlimitedUploadLimits(t *testing.T) {
	settings := newTestSettingsService(t)
	policy, err := settings.UploadPolicy()
	if err != nil {
		t.Fatalf("UploadPolicy returned error: %v", err)
	}
	policy.AnonymousMaxUploadMB = -1
	policy.AnonymousDailyUploadMB = -1
	policy.UserDailyUploadMB = -1
	if err := settings.UpdateUploadPolicy(policy); err != nil {
		t.Fatalf("UpdateUploadPolicy rejected -1 unlimited upload limits: %v", err)
	}
	next, err := settings.UploadPolicy()
	if err != nil {
		t.Fatalf("UploadPolicy returned error: %v", err)
	}
	if next.AnonymousMaxUploadMB != -1 || next.AnonymousDailyUploadMB != -1 || next.UserDailyUploadMB != -1 {
		t.Fatalf("unlimited upload limits were not persisted: %+v", next)
	}
	if got := FormatMegabytesLabel(-1); got != "unlimited" {
		t.Fatalf("FormatMegabytesLabel(-1) = %q, want unlimited", got)
	}
}

func TestDailyUsageAndCleanup(t *testing.T) {
	settings := newTestSettingsService(t)
	now := time.Date(2026, 5, 30, 12, 0, 0, 0, time.UTC)
	if err := settings.AddUsage("ip", "127.0.0.1", 1024, now); err != nil {
		t.Fatalf("AddUsage returned error: %v", err)
	}
	if err := settings.AddUsage("ip", "127.0.0.1", 2048, now); err != nil {
		t.Fatalf("AddUsage returned error: %v", err)
	}
	usage, err := settings.UsageForIP("127.0.0.1", now)
	if err != nil {
		t.Fatalf("UsageForIP returned error: %v", err)
	}
	if usage.UploadedBytes != 3072 {
		t.Fatalf("UploadedBytes = %d, want 3072", usage.UploadedBytes)
	}

	if err := settings.CleanupUsage(now.AddDate(0, 0, 31), 30); err != nil {
		t.Fatalf("CleanupUsage returned error: %v", err)
	}
	usage, err = settings.UsageForIP("127.0.0.1", now)
	if err != nil {
		t.Fatalf("UsageForIP returned error: %v", err)
	}
	if usage.UploadedBytes != 0 {
		t.Fatalf("UploadedBytes after cleanup = %d, want 0", usage.UploadedBytes)
	}
}

func TestEffectiveUserPolicyUsesOverridesAndInheritance(t *testing.T) {
	settings := newTestSettingsService(t)
	policy, err := settings.UploadPolicy()
	if err != nil {
		t.Fatalf("UploadPolicy returned error: %v", err)
	}
	policy.UserDailyUploadMB = 100
	policy.DefaultUserStorageMB = 200
	policy.UserMaxDays = 30
	policy.UserDailyBoxes = 40
	policy.UserActiveBoxes = 50
	policy.UserStorageBackend = "local"

	overrideDaily := 300.0
	overrideQuota := 0.0
	overrideDays := 12
	overrideBackend := "bucket-1"
	user := User{
		ID: "user-1",
		Policy: UserPolicy{
			DailyUploadMB:    &overrideDaily,
			StorageQuotaMB:   &overrideQuota,
			MaxDays:          &overrideDays,
			StorageBackendID: &overrideBackend,
		},
	}
	effective := settings.EffectivePolicyForUser(policy, user)
	if effective.DailyUploadMB != overrideDaily || effective.MaxDays != overrideDays || effective.StorageBackendID != overrideBackend {
		t.Fatalf("effective policy did not use overrides: %+v", effective)
	}
	if effective.StorageQuotaSet {
		t.Fatalf("zero storage quota override should mean unlimited: %+v", effective)
	}
	if effective.DailyBoxes != policy.UserDailyBoxes || effective.ActiveBoxes != policy.UserActiveBoxes {
		t.Fatalf("effective policy did not inherit box caps: %+v", effective)
	}
}

func newTestSettingsService(t *testing.T) *SettingsService {
	t.Helper()
	root := t.TempDir()
	upload, err := NewUploadService(1024*1024, filepath.Join(root, "data"), "http://example.test", slog.Default())
	if err != nil {
		t.Fatalf("NewUploadService returned error: %v", err)
	}
	t.Cleanup(func() {
		if err := upload.Close(); err != nil {
			t.Fatalf("Close returned error: %v", err)
		}
	})
	settings, err := NewSettingsService(upload.DB(), config.SettingsDefaults{
		AnonymousUploadsEnabled: true,
		AnonymousMaxUploadMB:    512,
		AnonymousDailyUploadMB:  2048,
		UserDailyUploadMB:       8192,
		DefaultUserStorageMB:    51200,
		UsageRetentionDays:      30,
	})
	if err != nil {
		t.Fatalf("NewSettingsService returned error: %v", err)
	}
	return settings
}
