package service

import (
	"context"
	"sync"
	"testing"

	"git.threesix.ai/jordan/slack5-1770541397/pkg/logging"
	"git.threesix.ai/jordan/slack5-1770541397/services/preferences-api/internal/domain"
	"git.threesix.ai/jordan/slack5-1770541397/services/preferences-api/internal/port"
)

// mockPreferencesRepository implements port.PreferencesRepository for testing.
type mockPreferencesRepository struct {
	mu    sync.RWMutex
	store map[domain.UserID]*domain.UserPreferences
}

var _ port.PreferencesRepository = (*mockPreferencesRepository)(nil)

func newMockPreferencesRepository() *mockPreferencesRepository {
	return &mockPreferencesRepository{
		store: make(map[domain.UserID]*domain.UserPreferences),
	}
}

func (m *mockPreferencesRepository) Get(ctx context.Context, userID domain.UserID) (*domain.UserPreferences, error) {
	m.mu.RLock()
	defer m.mu.RUnlock()

	prefs, ok := m.store[userID]
	if !ok {
		return nil, domain.ErrPreferencesNotFound
	}
	copy := *prefs
	return &copy, nil
}

func (m *mockPreferencesRepository) Upsert(ctx context.Context, prefs *domain.UserPreferences) error {
	m.mu.Lock()
	defer m.mu.Unlock()

	copy := *prefs
	m.store[prefs.UserID] = &copy
	return nil
}

func TestPreferencesService_GetPreferences(t *testing.T) {
	repo := newMockPreferencesRepository()
	svc := NewPreferencesService(repo, logging.Nop())

	t.Run("returns defaults when no preferences exist", func(t *testing.T) {
		prefs, err := svc.GetPreferences(context.Background(), "user-123")
		if err != nil {
			t.Fatalf("unexpected error: %v", err)
		}
		if prefs.UserID != "user-123" {
			t.Errorf("expected user_id 'user-123', got '%s'", prefs.UserID)
		}
		if prefs.Preferences.Theme != "system" {
			t.Errorf("expected default theme 'system', got '%s'", prefs.Preferences.Theme)
		}
		if prefs.Preferences.Language != "en" {
			t.Errorf("expected default language 'en', got '%s'", prefs.Preferences.Language)
		}
		if !prefs.Preferences.Notifications.Email {
			t.Error("expected default notifications.email to be true")
		}
		if !prefs.Preferences.Notifications.Push {
			t.Error("expected default notifications.push to be true")
		}
		if prefs.Preferences.Notifications.SMS {
			t.Error("expected default notifications.sms to be false")
		}
	})

	t.Run("returns stored preferences", func(t *testing.T) {
		// Store preferences first
		_, err := svc.SetPreferences(context.Background(), "user-456", domain.Preferences{
			Theme:    "dark",
			Language: "fr",
			Notifications: domain.NotificationPreferences{
				Email: false,
				Push:  true,
				SMS:   true,
			},
		})
		if err != nil {
			t.Fatalf("unexpected error setting preferences: %v", err)
		}

		prefs, err := svc.GetPreferences(context.Background(), "user-456")
		if err != nil {
			t.Fatalf("unexpected error: %v", err)
		}
		if prefs.Preferences.Theme != "dark" {
			t.Errorf("expected theme 'dark', got '%s'", prefs.Preferences.Theme)
		}
		if prefs.Preferences.Language != "fr" {
			t.Errorf("expected language 'fr', got '%s'", prefs.Preferences.Language)
		}
		if prefs.Preferences.Notifications.Email {
			t.Error("expected notifications.email to be false")
		}
		if prefs.Preferences.Notifications.SMS != true {
			t.Error("expected notifications.sms to be true")
		}
	})
}

func TestPreferencesService_SetPreferences(t *testing.T) {
	repo := newMockPreferencesRepository()
	svc := NewPreferencesService(repo, logging.Nop())

	t.Run("sets valid preferences", func(t *testing.T) {
		prefs, err := svc.SetPreferences(context.Background(), "user-789", domain.Preferences{
			Theme:    "light",
			Language: "en",
			Notifications: domain.NotificationPreferences{
				Email: true,
				Push:  false,
				SMS:   false,
			},
		})
		if err != nil {
			t.Fatalf("unexpected error: %v", err)
		}
		if prefs.UserID != "user-789" {
			t.Errorf("expected user_id 'user-789', got '%s'", prefs.UserID)
		}
		if prefs.Preferences.Theme != "light" {
			t.Errorf("expected theme 'light', got '%s'", prefs.Preferences.Theme)
		}
		if prefs.UpdatedAt.IsZero() {
			t.Error("expected non-zero updated_at")
		}
	})

	t.Run("rejects invalid theme", func(t *testing.T) {
		_, err := svc.SetPreferences(context.Background(), "user-789", domain.Preferences{
			Theme:    "neon",
			Language: "en",
		})
		if err != domain.ErrInvalidTheme {
			t.Errorf("expected ErrInvalidTheme, got %v", err)
		}
	})

	t.Run("rejects language exceeding max length", func(t *testing.T) {
		_, err := svc.SetPreferences(context.Background(), "user-789", domain.Preferences{
			Theme:    "dark",
			Language: "abcdefghijk", // 11 chars
		})
		if err != domain.ErrInvalidLanguage {
			t.Errorf("expected ErrInvalidLanguage, got %v", err)
		}
	})

	t.Run("allows empty theme", func(t *testing.T) {
		prefs, err := svc.SetPreferences(context.Background(), "user-000", domain.Preferences{
			Theme:    "",
			Language: "en",
		})
		if err != nil {
			t.Fatalf("unexpected error: %v", err)
		}
		if prefs.Preferences.Theme != "" {
			t.Errorf("expected empty theme, got '%s'", prefs.Preferences.Theme)
		}
	})

	t.Run("allows max length language", func(t *testing.T) {
		_, err := svc.SetPreferences(context.Background(), "user-000", domain.Preferences{
			Theme:    "dark",
			Language: "abcdefghij", // exactly 10 chars
		})
		if err != nil {
			t.Fatalf("unexpected error: %v", err)
		}
	})
}
