package notifications

import (
	"context"
	"testing"

	"switchboard-core/models"
)

// mockPrefStore implements store.NotificationPreferenceStore for testing.
type mockPrefStore struct {
	prefs map[string]map[string]*models.NotificationPreference // userID → type → pref
}

func newMockPrefStore() *mockPrefStore {
	return &mockPrefStore{prefs: make(map[string]map[string]*models.NotificationPreference)}
}

func (m *mockPrefStore) Get(_ context.Context, userID, notifType string) (*models.NotificationPreference, error) {
	if userPrefs, ok := m.prefs[userID]; ok {
		if p, ok := userPrefs[notifType]; ok {
			return p, nil
		}
	}
	return nil, nil
}

func (m *mockPrefStore) ListForUser(_ context.Context, userID string) ([]models.NotificationPreference, error) {
	var result []models.NotificationPreference
	if userPrefs, ok := m.prefs[userID]; ok {
		for _, p := range userPrefs {
			result = append(result, *p)
		}
	}
	return result, nil
}

func (m *mockPrefStore) Upsert(_ context.Context, pref *models.NotificationPreference) error {
	if m.prefs[pref.UserID] == nil {
		m.prefs[pref.UserID] = make(map[string]*models.NotificationPreference)
	}
	m.prefs[pref.UserID][pref.Type] = pref
	return nil
}

func (m *mockPrefStore) Delete(_ context.Context, userID, notifType string) error {
	if userPrefs, ok := m.prefs[userID]; ok {
		delete(userPrefs, notifType)
	}
	return nil
}

func (m *mockPrefStore) set(userID, notifType string, inApp, email bool) {
	if m.prefs[userID] == nil {
		m.prefs[userID] = make(map[string]*models.NotificationPreference)
	}
	m.prefs[userID][notifType] = &models.NotificationPreference{
		UserID: userID,
		Type:   notifType,
		InApp:  inApp,
		Email:  email,
	}
}

func TestResolvePrefs_SystemDefault(t *testing.T) {
	svc := &Service{prefStore: newMockPrefStore()}
	p := svc.resolvePrefs(context.Background(), "user1", "kb.ready")
	if !p.InApp || p.Email {
		t.Errorf("expected system default (in_app=true, email=false), got %+v", p)
	}
}

func TestResolvePrefs_NoPrefStore(t *testing.T) {
	svc := &Service{prefStore: nil}
	p := svc.resolvePrefs(context.Background(), "user1", "kb.ready")
	if !p.InApp || p.Email {
		t.Errorf("expected system default when no pref store, got %+v", p)
	}
}

func TestResolvePrefs_SpecificType(t *testing.T) {
	ps := newMockPrefStore()
	ps.set("user1", "kb.ready", true, true)
	svc := &Service{prefStore: ps}

	p := svc.resolvePrefs(context.Background(), "user1", "kb.ready")
	if !p.InApp || !p.Email {
		t.Errorf("expected specific pref (in_app=true, email=true), got %+v", p)
	}
}

func TestResolvePrefs_WildcardFallback(t *testing.T) {
	ps := newMockPrefStore()
	ps.set("user1", "*", false, true) // user turned off in-app, turned on email globally
	svc := &Service{prefStore: ps}

	// No specific "kb.ready" pref → falls to wildcard
	p := svc.resolvePrefs(context.Background(), "user1", "kb.ready")
	if p.InApp || !p.Email {
		t.Errorf("expected wildcard pref (in_app=false, email=true), got %+v", p)
	}
}

func TestResolvePrefs_SpecificOverridesWildcard(t *testing.T) {
	ps := newMockPrefStore()
	ps.set("user1", "*", false, true)          // wildcard: no in-app, yes email
	ps.set("user1", "kb.ready", true, false)    // specific: yes in-app, no email
	svc := &Service{prefStore: ps}

	p := svc.resolvePrefs(context.Background(), "user1", "kb.ready")
	if !p.InApp || p.Email {
		t.Errorf("specific should override wildcard, got %+v", p)
	}
}

func TestResolvePrefs_DifferentUsersIsolated(t *testing.T) {
	ps := newMockPrefStore()
	ps.set("user1", "kb.ready", false, true)
	svc := &Service{prefStore: ps}

	// user2 has no prefs → system default
	p := svc.resolvePrefs(context.Background(), "user2", "kb.ready")
	if !p.InApp || p.Email {
		t.Errorf("user2 should get system default, got %+v", p)
	}
}

func TestResolvePrefs_DisableBoth(t *testing.T) {
	ps := newMockPrefStore()
	ps.set("user1", "kb.ready", false, false)
	svc := &Service{prefStore: ps}

	p := svc.resolvePrefs(context.Background(), "user1", "kb.ready")
	if p.InApp || p.Email {
		t.Errorf("expected both disabled, got %+v", p)
	}
}
