// Package postgres provides PostgreSQL implementations of port interfaces.
package postgres

import (
	"context"
	"database/sql"
	"encoding/json"
	"errors"
	"time"

	"github.com/jmoiron/sqlx"

	"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"
)

// preferencesRow represents a row in the user_preferences table.
type preferencesRow struct {
	UserID      string    `db:"user_id"`
	Preferences []byte    `db:"preferences"`
	UpdatedAt   time.Time `db:"updated_at"`
}

// PreferencesRepository implements port.PreferencesRepository using PostgreSQL.
type PreferencesRepository struct {
	db     *sqlx.DB
	logger *logging.Logger
}

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

// NewPreferencesRepository creates a new PostgreSQL preferences repository.
func NewPreferencesRepository(db *sqlx.DB, logger *logging.Logger) *PreferencesRepository {
	return &PreferencesRepository{
		db:     db,
		logger: logger.WithComponent("PreferencesRepository"),
	}
}

// Get retrieves preferences for a user by ID.
// Returns domain.ErrPreferencesNotFound if no row exists.
func (r *PreferencesRepository) Get(ctx context.Context, userID domain.UserID) (*domain.UserPreferences, error) {
	var row preferencesRow
	err := r.db.GetContext(ctx, &row,
		`SELECT user_id, preferences, updated_at FROM user_preferences WHERE user_id = $1`,
		string(userID),
	)
	if err != nil {
		if errors.Is(err, sql.ErrNoRows) {
			return nil, domain.ErrPreferencesNotFound
		}
		return nil, err
	}

	var prefs domain.Preferences
	if err := json.Unmarshal(row.Preferences, &prefs); err != nil {
		return nil, err
	}

	return &domain.UserPreferences{
		UserID:      domain.UserID(row.UserID),
		Preferences: prefs,
		UpdatedAt:   row.UpdatedAt,
	}, nil
}

// Upsert creates or replaces preferences for a user.
func (r *PreferencesRepository) Upsert(ctx context.Context, prefs *domain.UserPreferences) error {
	prefsJSON, err := json.Marshal(prefs.Preferences)
	if err != nil {
		return err
	}

	_, err = r.db.ExecContext(ctx, `
		INSERT INTO user_preferences (user_id, preferences, updated_at)
		VALUES ($1, $2, $3)
		ON CONFLICT (user_id) DO UPDATE SET
			preferences = $2,
			updated_at = $3
	`, string(prefs.UserID), prefsJSON, prefs.UpdatedAt)

	return err
}
