package postgres import ( "context" "database/sql" "fmt" "time" "git.threesix.ai/jordan/slack5-1770574304/services/preferences-api/internal/domain" "git.threesix.ai/jordan/slack5-1770574304/services/preferences-api/internal/port" ) // Compile-time verification that PreferenceRepository implements port.PreferenceRepository. var _ port.PreferenceRepository = (*PreferenceRepository)(nil) // PreferenceRepository is a PostgreSQL implementation of port.PreferenceRepository. type PreferenceRepository struct { db *sql.DB } // NewPreferenceRepository creates a new PostgreSQL preference repository. // It ensures the schema exists on creation. func NewPreferenceRepository(db *sql.DB) (*PreferenceRepository, error) { r := &PreferenceRepository{db: db} if err := r.ensureSchema(); err != nil { return nil, fmt.Errorf("ensure schema: %w", err) } return r, nil } // ensureSchema creates the user_preferences table if it does not exist. func (r *PreferenceRepository) ensureSchema() error { _, err := r.db.Exec(` CREATE TABLE IF NOT EXISTS user_preferences ( user_id TEXT PRIMARY KEY, theme TEXT NOT NULL DEFAULT 'system', language TEXT NOT NULL DEFAULT 'en', notify_email BOOLEAN NOT NULL DEFAULT true, notify_push BOOLEAN NOT NULL DEFAULT true, notify_digest TEXT NOT NULL DEFAULT 'weekly', updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW() ) `) return err } // Get returns the preferences for a user by ID. // Returns nil, nil when no preferences exist. func (r *PreferenceRepository) Get(ctx context.Context, userID string) (*domain.UserPreferences, error) { var p domain.UserPreferences var updatedAt time.Time err := r.db.QueryRowContext(ctx, `SELECT user_id, theme, language, notify_email, notify_push, notify_digest, updated_at FROM user_preferences WHERE user_id = $1`, userID, ).Scan( &p.UserID, &p.Theme, &p.Language, &p.Notifications.Email, &p.Notifications.Push, &p.Notifications.Digest, &updatedAt, ) if err == sql.ErrNoRows { return nil, nil } if err != nil { return nil, fmt.Errorf("get preferences: %w", err) } p.UpdatedAt = updatedAt return &p, nil } // Upsert creates or replaces preferences for a user. func (r *PreferenceRepository) Upsert(ctx context.Context, prefs *domain.UserPreferences) error { _, err := r.db.ExecContext(ctx, ` INSERT INTO user_preferences (user_id, theme, language, notify_email, notify_push, notify_digest, updated_at) VALUES ($1, $2, $3, $4, $5, $6, NOW()) ON CONFLICT (user_id) DO UPDATE SET theme = $2, language = $3, notify_email = $4, notify_push = $5, notify_digest = $6, updated_at = NOW() `, prefs.UserID, prefs.Theme, prefs.Language, prefs.Notifications.Email, prefs.Notifications.Push, prefs.Notifications.Digest, ) if err != nil { return fmt.Errorf("upsert preferences: %w", err) } return nil }