package models

import (
	"testing"
	"time"

	"github.com/golang-jwt/jwt/v5"
)

func TestAccessTokenCreation(t *testing.T) {
	token := &AccessToken{
		Email:     TestEmail,
		SessionID: SessionID,
		Exp:       time.Now().Add(15 * time.Minute).Unix(),
		RegisteredClaims: jwt.RegisteredClaims{
			ExpiresAt: jwt.NewNumericDate(time.Now().Add(15 * time.Minute)),
		},
	}

	if token.Email != TestEmail {
		t.Errorf("Expected email 'test@example.com', got '%s'", token.Email)
	}

	if token.SessionID != SessionID {
		t.Errorf("Expected session ID 'session-123', got '%s'", token.SessionID)
	}

	if token.Exp == 0 {
		t.Error("Expected Exp to be set, got 0")
	}
}

func TestAccessTokenExpiration(t *testing.T) {
	expTime := time.Now().Add(15 * time.Minute)
	token := &AccessToken{
		Exp: expTime.Unix(),
		RegisteredClaims: jwt.RegisteredClaims{
			ExpiresAt: jwt.NewNumericDate(expTime),
		},
	}

	// Check if token is not expired
	if time.Now().Unix() > token.Exp {
		t.Error("Token should not be expired")
	}

	// Test expired token
	expiredToken := &AccessToken{
		Email:     TestEmail,
		SessionID: "session-456",
		Exp:       time.Now().Add(-1 * time.Hour).Unix(),
	}

	if expiredToken.Email != TestEmail {
		t.Errorf("Expected email 'test@example.com', got '%s'", expiredToken.Email)
	}

	if expiredToken.SessionID != "session-456" {
		t.Errorf("Expected session ID 'session-456', got '%s'", expiredToken.SessionID)
	}

	if time.Now().Unix() <= expiredToken.Exp {
		t.Error("Token should be expired")
	}
}

func TestJWTSessionCreation(t *testing.T) {
	now := time.Now()
	session := &JWTSession{
		ID:               "session-id-123",
		UserID:           "user-456",
		RefreshTokenHash: "hash123",
		UserAgent:        "Mozilla/5.0",
		IPAddress:        "192.168.1.1",
		CreatedAt:        now,
		UpdatedAt:        now,
		ExpiresAt:        now.Add(7 * 24 * time.Hour),
		IsRevoked:        false,
	}

	if session.ID != "session-id-123" {
		t.Errorf("Expected session ID 'session-id-123', got '%s'", session.ID)
	}

	if session.UserID != "user-456" {
		t.Errorf("Expected user ID 'user-456', got '%s'", session.UserID)
	}

	if session.RefreshTokenHash != "hash123" {
		t.Errorf("Expected refresh token hash 'hash123', got '%s'", session.RefreshTokenHash)
	}

	if session.UserAgent != "Mozilla/5.0" {
		t.Errorf("Expected user agent 'Mozilla/5.0', got '%s'", session.UserAgent)
	}

	if session.IPAddress != "192.168.1.1" {
		t.Errorf("Expected IP address '192.168.1.1', got '%s'", session.IPAddress)
	}

	if session.CreatedAt.IsZero() {
		t.Error("Expected CreatedAt to be set")
	}

	if session.UpdatedAt.IsZero() {
		t.Error("Expected UpdatedAt to be set")
	}

	if session.ExpiresAt.IsZero() {
		t.Error("Expected ExpiresAt to be set")
	}

	if session.IsRevoked {
		t.Error("Expected session to not be revoked")
	}
}

func TestJWTSessionIsExpired(t *testing.T) {
	now := time.Now()

	// Active session
	activeSession := &JWTSession{
		ID:        "active-session",
		ExpiresAt: now.Add(1 * time.Hour),
		IsRevoked: false,
	}

	if activeSession.ID != "active-session" {
		t.Errorf("Expected ID 'active-session', got '%s'", activeSession.ID)
	}

	if activeSession.IsRevoked {
		t.Error("Active session should not be revoked")
	}

	if activeSession.ExpiresAt.Before(now) {
		t.Error("Active session should not be expired")
	}

	// Expired session
	expiredSession := &JWTSession{
		ID:        "expired-session",
		ExpiresAt: now.Add(-1 * time.Hour),
		IsRevoked: false,
	}

	if expiredSession.ID != "expired-session" {
		t.Errorf("Expected ID 'expired-session', got '%s'", expiredSession.ID)
	}

	if expiredSession.IsRevoked {
		t.Error("Expired session should not be marked as revoked initially")
	}

	if !expiredSession.ExpiresAt.Before(now) {
		t.Error("Expired session should be marked as expired")
	}
}

func TestJWTSessionRevokedStatus(t *testing.T) {
	session := &JWTSession{
		ID:        "test-session",
		IsRevoked: false,
	}

	if session.ID != "test-session" {
		t.Errorf("Expected ID 'test-session', got '%s'", session.ID)
	}

	if session.IsRevoked {
		t.Error("New session should not be revoked")
	}

	// Simulate revocation
	session.IsRevoked = true

	if !session.IsRevoked {
		t.Error("Session should be revoked after setting IsRevoked to true")
	}
}

func TestExpiredSessionCreation(t *testing.T) {
	expiredSession := ExpiredSession{
		ID:               "expired-id-123",
		UserID:           "user-789",
		RefreshTokenHash: "expired-hash",
	}

	if expiredSession.ID != "expired-id-123" {
		t.Errorf("Expected ID 'expired-id-123', got '%s'", expiredSession.ID)
	}

	if expiredSession.UserID != "user-789" {
		t.Errorf("Expected UserID 'user-789', got '%s'", expiredSession.UserID)
	}

	if expiredSession.RefreshTokenHash != "expired-hash" {
		t.Errorf("Expected RefreshTokenHash 'expired-hash', got '%s'", expiredSession.RefreshTokenHash)
	}
}

func TestJWTSessionUpdateActivity(t *testing.T) {
	now := time.Now()
	session := &JWTSession{
		ID:        SessionID,
		CreatedAt: now,
		UpdatedAt: now,
	}

	if session.ID != SessionID {
		t.Errorf("expected session ID %s, got %s", SessionID, session.ID)
	}

	// Simulate activity update
	time.Sleep(10 * time.Millisecond)
	newUpdateTime := time.Now()
	session.UpdatedAt = newUpdateTime

	if !session.UpdatedAt.After(session.CreatedAt) {
		t.Error("UpdatedAt should be after CreatedAt after activity update")
	}

	if session.UpdatedAt.Before(now) {
		t.Error("UpdatedAt should be more recent than original time")
	}
}

func TestJWTSessionSecurityFields(t *testing.T) {
	session := &JWTSession{
		ID:        SessionID,
		UserAgent: "Mozilla/5.0 (Windows NT 10.0; Win64; x64)",
		IPAddress: "192.168.1.100",
	}

	if session.ID != SessionID {
		t.Errorf("Expected ID %s", session.ID)
	}

	// Test user agent validation
	expectedUserAgent := "Mozilla/5.0 (Windows NT 10.0; Win64; x64)"
	if session.UserAgent != expectedUserAgent {
		t.Errorf("Expected user agent '%s', got '%s'", expectedUserAgent, session.UserAgent)
	}

	// Test IP address validation
	expectedIP := "192.168.1.100"
	if session.IPAddress != expectedIP {
		t.Errorf("Expected IP address '%s', got '%s'", expectedIP, session.IPAddress)
	}

	// Test mismatch scenario
	if session.UserAgent == "Different Browser" {
		t.Error("User agent should not match different value")
	}

	if session.IPAddress == "10.0.0.1" {
		t.Error("IP address should not match different value")
	}
}

func TestJWTSessionTimeValidation(t *testing.T) {
	now := time.Now()
	futureTime := now.Add(7 * 24 * time.Hour)

	session := &JWTSession{
		ID:        SessionID,
		CreatedAt: now,
		UpdatedAt: now,
		ExpiresAt: futureTime,
	}

	if session.ID != SessionID {
		t.Errorf("Expected ID %s, got %s", SessionID, session.ID)
	}

	// CreatedAt should not be after UpdatedAt
	if session.CreatedAt.After(session.UpdatedAt) {
		t.Error("CreatedAt should not be after UpdatedAt")
	}

	// ExpiresAt should be after CreatedAt
	if !session.ExpiresAt.After(session.CreatedAt) {
		t.Error("ExpiresAt should be after CreatedAt")
	}

	// Session should not be expired
	if session.ExpiresAt.Before(now) {
		t.Error("Session should not be expired yet")
	}
}
