package domain

import (
	"testing"
	"time"
)

func TestToken_AccessTokenExpires(t *testing.T) {
	tests := []struct {
		name      string
		expiresIn int
		want      time.Duration
	}{
		{"zero expiration", 0, 0},
		{"1 second expiration", 1, time.Second},
		{"60 seconds expiration", 60, time.Minute},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			token := Token{ExpiresIn: tt.expiresIn}
			if got := token.AccessTokenExpires(); got != tt.want {
				t.Errorf("AccessTokenExpires() = %v, want %v", got, tt.want)
			}
		})
	}
}

func TestToken_RefreshTokenExpires(t *testing.T) {
	tests := []struct {
		name           string
		refreshExpires int
		want           time.Duration
	}{
		{"zero expiration", 0, 0},
		{"1 second expiration", 1, time.Second},
		{"90 seconds expiration", 90, 90 * time.Second},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			token := Token{RefreshExpiresIn: tt.refreshExpires}
			if got := token.RefreshTokenExpires(); got != tt.want {
				t.Errorf("RefreshTokenExpires() = %v, want %v", got, tt.want)
			}
		})
	}
}

func TestToken_RefreshTokenExpiresUnix(t *testing.T) {
	tests := []struct {
		name           string
		refreshExpires int
	}{
		{"zero expiration", 0},
		{"10 seconds expiration", 10},
		{"60 seconds expiration", 60},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			token := Token{RefreshExpiresIn: tt.refreshExpires}
			got := token.RefreshTokenExpiresUnix()
			want := time.Now().Add(time.Duration(tt.refreshExpires) * time.Second).UnixNano()

			// 確保計算的時間戳在合理範圍內
			if got < want-1e9 || got > want+1e9 {
				t.Errorf("RefreshTokenExpiresUnix() = %v, want %v (±1 second tolerance)", got, want)
			}
		})
	}
}

func TestToken_IsExpires(t *testing.T) {
	now := time.Now()
	tests := []struct {
		name           string
		accessCreateAt time.Time
		expiresIn      int
		want           bool
	}{
		{"not expired", now.Add(-5 * time.Minute), 600, false},  // 10-minute expiry, created 5 minutes ago
		{"just expired", now.Add(-10 * time.Minute), 600, true}, // 10-minute expiry, created 10 minutes ago
		{"already expired", now.Add(-15 * time.Minute), 600, true},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			token := Token{AccessCreateAt: tt.accessCreateAt, ExpiresIn: tt.expiresIn}
			if got := token.IsExpires(); got != tt.want {
				t.Errorf("IsExpires() = %v, want %v", got, tt.want)
			}
		})
	}
}

func TestToken_RedisExpiredSec(t *testing.T) {
	now := time.Now().Unix()
	tests := []struct {
		name      string
		expiresIn int
	}{
		{"zero expiration", 0},
		{"future expiration", int(now + 3600)}, // Expires in 1 hour
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			token := Token{ExpiresIn: tt.expiresIn}
			got := token.RedisExpiredSec()
			want := time.Unix(int64(tt.expiresIn), 0).Sub(time.Now().UTC()).Seconds()

			if float64(got) < want-1 || float64(got) > want+1 {
				t.Errorf("RedisExpiredSec() = %v, want ~%v (±1 second tolerance)", got, int64(want))
			}
		})
	}
}

func TestToken_RedisRefreshExpiredSec(t *testing.T) {
	now := time.Now().Unix()
	tests := []struct {
		name           string
		refreshExpires int
	}{
		{"zero refresh expiration", 0},
		{"future refresh expiration", int(now + 7200)}, // Expires in 2 hours
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			token := Token{RefreshExpiresIn: tt.refreshExpires}
			got := token.RedisRefreshExpiredSec()
			want := time.Unix(int64(tt.refreshExpires), 0).Sub(time.Now().UTC()).Seconds()

			if float64(got) < want-1 || float64(got) > want+1 {
				t.Errorf("RedisRefreshExpiredSec() = %v, want ~%v (±1 second tolerance)", got, int64(want))
			}
		})
	}
}
