package postgres

import (
	"context"
	"fmt"

	"chat-switchboard/models"
)

type UserModelSettingsStore struct{}

func NewUserModelSettingsStore() *UserModelSettingsStore { return &UserModelSettingsStore{} }

func (s *UserModelSettingsStore) GetForUser(ctx context.Context, userID string) ([]models.UserModelSetting, error) {
	rows, err := DB.QueryContext(ctx, `
		SELECT id, user_id, model_id, provider_config_id, COALESCE(hidden, false), preferred_temperature, preferred_max_tokens,
			COALESCE(sort_order, 0), created_at, updated_at
		FROM user_model_settings WHERE user_id = $1 ORDER BY sort_order, model_id`, userID)
	if err != nil {
		return nil, err
	}
	defer rows.Close()

	var result []models.UserModelSetting
	for rows.Next() {
		var s models.UserModelSetting
		var prefTemp, prefMaxTokens interface{}
		err := rows.Scan(
			&s.ID, &s.UserID, &s.ModelID, &s.ProviderConfigID, &s.Hidden,
			&prefTemp, &prefMaxTokens,
			&s.SortOrder, &s.CreatedAt, &s.UpdatedAt,
		)
		if err != nil {
			return nil, err
		}
		if f, ok := prefTemp.(float64); ok {
			s.PreferredTemperature = &f
		}
		if n, ok := prefMaxTokens.(int64); ok {
			v := int(n)
			s.PreferredMaxTokens = &v
		}
		result = append(result, s)
	}
	return result, rows.Err()
}

// GetHiddenModelIDs returns a map of provider_config_id:model_id → true for all hidden models.
func (s *UserModelSettingsStore) GetHiddenModelIDs(ctx context.Context, userID string) (map[string]bool, error) {
	rows, err := DB.QueryContext(ctx,
		"SELECT model_id, COALESCE(provider_config_id::text, '') FROM user_model_settings WHERE user_id = $1 AND hidden = true",
		userID)
	if err != nil {
		return nil, err
	}
	defer rows.Close()

	result := make(map[string]bool)
	for rows.Next() {
		var modelID, provCfgID string
		if err := rows.Scan(&modelID, &provCfgID); err != nil {
			return nil, err
		}
		result[models.CompositeModelKey(provCfgID, modelID)] = true
	}
	return result, rows.Err()
}

// Set upserts a single user model setting.
func (s *UserModelSettingsStore) Set(ctx context.Context, userID, modelID string, providerConfigID *string, patch models.UserModelSettingPatch) error {
	b := NewUpdate("user_model_settings")
	if patch.Hidden != nil {
		b.Set("hidden", *patch.Hidden)
	}
	if patch.PreferredTemperature != nil {
		b.Set("preferred_temperature", models.NullFloat(patch.PreferredTemperature))
	}
	if patch.PreferredMaxTokens != nil {
		b.Set("preferred_max_tokens", models.NullInt(patch.PreferredMaxTokens))
	}
	if patch.SortOrder != nil {
		b.Set("sort_order", *patch.SortOrder)
	}

	if !b.HasSets() {
		return nil
	}

	// Use upsert: insert if not exists, update if exists
	_, err := DB.ExecContext(ctx, `
		INSERT INTO user_model_settings (user_id, model_id, provider_config_id, hidden, preferred_temperature, preferred_max_tokens, sort_order)
		VALUES ($1, $2, $3, COALESCE($4, false), $5, $6, COALESCE($7, 0))
		ON CONFLICT (user_id, model_id, provider_config_id)
		DO UPDATE SET
			hidden = COALESCE($4, user_model_settings.hidden),
			preferred_temperature = COALESCE($5, user_model_settings.preferred_temperature),
			preferred_max_tokens = COALESCE($6, user_model_settings.preferred_max_tokens),
			sort_order = COALESCE($7, user_model_settings.sort_order)`,
		userID, modelID, providerConfigID,
		patchBoolOrNil(patch.Hidden),
		patchFloat64OrNil(patch.PreferredTemperature),
		patchIntOrNil(patch.PreferredMaxTokens),
		patchIntOrNil(patch.SortOrder),
	)
	return err
}

// BulkSetHidden sets the hidden state for multiple model+provider pairs at once.
func (s *UserModelSettingsStore) BulkSetHidden(ctx context.Context, userID string, entries []models.HiddenEntry, hidden bool) error {
	if len(entries) == 0 {
		return nil
	}

	for _, entry := range entries {
		_, err := DB.ExecContext(ctx, `
			INSERT INTO user_model_settings (user_id, model_id, provider_config_id, hidden)
			VALUES ($1, $2, $3, $4)
			ON CONFLICT (user_id, model_id, provider_config_id) DO UPDATE SET hidden = $4`,
			userID, entry.ModelID, entry.ProviderConfigID, hidden)
		if err != nil {
			return fmt.Errorf("set hidden for %s:%s: %w", entry.ProviderConfigID, entry.ModelID, err)
		}
	}
	return nil
}

// ── Helpers ─────────────────────────────────

func patchBoolOrNil(b *bool) interface{} {
	if b == nil {
		return nil
	}
	return *b
}

func patchFloat64OrNil(f *float64) interface{} {
	if f == nil {
		return nil
	}
	return *f
}

func patchIntOrNil(i *int) interface{} {
	if i == nil {
		return nil
	}
	return *i
}

