package db

import (
	"path/filepath"
	"testing"
	"time"
)

func openTestDB(t *testing.T) *DB {
	t.Helper()
	database, err := Open(filepath.Join(t.TempDir(), "test.db"))
	if err != nil {
		t.Fatal(err)
	}
	t.Cleanup(func() { database.Close() })
	return database
}

// TestBlacklistIPEscalation confirms repeat offenses double the block duration up to the cap.
func TestBlacklistIPEscalation(t *testing.T) {
	d := openTestDB(t)
	const ip = "203.0.113.7"

	wantHours := []int{12, 24, 48, 96, 168, 168} // caps at 168 (7 days)
	for i, want := range wantHours {
		if err := d.BlacklistIP(ip, "test", 12, 168); err != nil {
			t.Fatalf("offense %d: %v", i+1, err)
		}
		list, err := d.ListBlacklist()
		if err != nil {
			t.Fatal(err)
		}
		if len(list) != 1 {
			t.Fatalf("offense %d: expected 1 entry, got %d", i+1, len(list))
		}
		e := list[0]
		if e.OffenseCount != i+1 {
			t.Errorf("offense %d: OffenseCount = %d, want %d", i+1, e.OffenseCount, i+1)
		}
		gotHours := e.ExpiresAt.Sub(e.BlacklistedAt).Hours()
		if diff := gotHours - float64(want); diff < -1 || diff > 1 {
			t.Errorf("offense %d: duration = %.1fh, want ~%dh", i+1, gotHours, want)
		}
	}
}

// TestCountFailedAuthAttemptsByIPMatchesCurrentTimestamp guards against the exact
// SQLite time.Time/CURRENT_TIMESTAMP format mismatch already found once in
// CountRecentFailedAttempts: a row inserted via CURRENT_TIMESTAMP must be found by a
// since-cutoff comparison using a Go-side time.Time a moment earlier.
func TestCountFailedAuthAttemptsByIPMatchesCurrentTimestamp(t *testing.T) {
	d := openTestDB(t)
	const ip = "198.51.100.9"
	since := time.Now().Add(-1 * time.Minute)

	if err := d.LogAuthAttempt("sender", "someone@example.com", ip, false, "bad password"); err != nil {
		t.Fatal(err)
	}

	n, err := d.CountFailedAuthAttemptsByIP(ip, since)
	if err != nil {
		t.Fatal(err)
	}
	if n != 1 {
		t.Fatalf("CountFailedAuthAttemptsByIP = %d, want 1 (CURRENT_TIMESTAMP/time.Time format mismatch?)", n)
	}

	blacklisted, err := d.IsIPBlacklisted(ip)
	if err != nil {
		t.Fatal(err)
	}
	if blacklisted {
		t.Fatal("IP should not be blacklisted yet")
	}
}

func TestIPAbuseWhitelist(t *testing.T) {
	d := openTestDB(t)
	const ip = "192.0.2.55"

	whitelisted, err := d.IsIPAbuseWhitelisted(ip)
	if err != nil {
		t.Fatal(err)
	}
	if whitelisted {
		t.Fatal("should not be whitelisted before AddAbuseWhitelist")
	}

	if err := d.AddAbuseWhitelist(ip, "trusted scanner"); err != nil {
		t.Fatal(err)
	}
	whitelisted, err = d.IsIPAbuseWhitelisted(ip)
	if err != nil {
		t.Fatal(err)
	}
	if !whitelisted {
		t.Fatal("should be whitelisted after AddAbuseWhitelist")
	}
}
