package mysql

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

	"hyapp/pkg/appcode"
	"hyapp/pkg/xerr"
	"hyapp/services/wallet-service/internal/domain/ledger"
)

// UpdateVipUserSettings 用一条 partial upsert 原子修改用户明确提交的开关。
// INSERT 分支必须使用与无行读取完全相同的产品默认；UPDATE 分支则保留原值，
// 避免用户首次只改通知时意外开启三项隐私权益，也避免两个并发 PATCH 互相覆盖。
func (r *Repository) UpdateVipUserSettings(ctx context.Context, command ledger.UpdateVipUserSettingsCommand) (ledger.VipUserSettings, error) {
	if r == nil || r.db == nil {
		return ledger.VipUserSettings{}, xerr.New(xerr.Unavailable, "mysql repository is not configured")
	}
	ctx = contextWithCommandApp(ctx, command.AppCode)
	command.AppCode = appcode.FromContext(ctx)
	if command.UserID <= 0 || (command.RoomEntryNoticeEnabled == nil && command.OnlineGlobalNoticeEnabled == nil &&
		command.HideProfileDataEnabled == nil && command.AnonymousProfileVisitEnabled == nil &&
		command.LeaderboardInvisibleEnabled == nil) {
		return ledger.VipUserSettings{}, xerr.New(xerr.InvalidArgument, "vip user settings update is incomplete")
	}

	tx, err := r.db.BeginTx(ctx, nil)
	if err != nil {
		return ledger.VipUserSettings{}, err
	}
	defer func() { _ = tx.Rollback() }()
	nowMS := time.Now().UTC().UnixMilli()
	roomValue := true
	roomProvided := command.RoomEntryNoticeEnabled != nil
	if roomProvided {
		roomValue = *command.RoomEntryNoticeEnabled
	}
	onlineValue := true
	onlineProvided := command.OnlineGlobalNoticeEnabled != nil
	if onlineProvided {
		onlineValue = *command.OnlineGlobalNoticeEnabled
	}
	hideProfileValue := false
	hideProfileProvided := command.HideProfileDataEnabled != nil
	if hideProfileProvided {
		hideProfileValue = *command.HideProfileDataEnabled
	}
	anonymousVisitValue := false
	anonymousVisitProvided := command.AnonymousProfileVisitEnabled != nil
	if anonymousVisitProvided {
		anonymousVisitValue = *command.AnonymousProfileVisitEnabled
	}
	leaderboardInvisibleValue := false
	leaderboardInvisibleProvided := command.LeaderboardInvisibleEnabled != nil
	if leaderboardInvisibleProvided {
		leaderboardInvisibleValue = *command.LeaderboardInvisibleEnabled
	}
	if _, err := tx.ExecContext(ctx, `
		INSERT INTO user_vip_settings (
		  app_code, user_id, room_entry_notice_enabled, online_global_notice_enabled,
		  hide_profile_data_enabled, anonymous_profile_visit_enabled, leaderboard_invisible_enabled,
		  created_at_ms, updated_at_ms
		) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)
		ON DUPLICATE KEY UPDATE
		  room_entry_notice_enabled = IF(?, ?, room_entry_notice_enabled),
		  online_global_notice_enabled = IF(?, ?, online_global_notice_enabled),
		  hide_profile_data_enabled = IF(?, ?, hide_profile_data_enabled),
		  anonymous_profile_visit_enabled = IF(?, ?, anonymous_profile_visit_enabled),
		  leaderboard_invisible_enabled = IF(?, ?, leaderboard_invisible_enabled),
		  updated_at_ms = ?`,
		command.AppCode, command.UserID, roomValue, onlineValue, hideProfileValue,
		anonymousVisitValue, leaderboardInvisibleValue, nowMS, nowMS,
		roomProvided, roomValue, onlineProvided, onlineValue,
		hideProfileProvided, hideProfileValue, anonymousVisitProvided, anonymousVisitValue,
		leaderboardInvisibleProvided, leaderboardInvisibleValue, nowMS,
	); err != nil {
		return ledger.VipUserSettings{}, err
	}
	settings, err := r.queryVipUserSettings(ctx, tx, command.UserID, false)
	if err != nil {
		return ledger.VipUserSettings{}, err
	}
	if err := tx.Commit(); err != nil {
		return ledger.VipUserSettings{}, err
	}
	return settings, nil
}

// queryVipUserSettings 统一处理“无行即产品默认”。调用方可以传事务，从而让 VipState 的
// program、会员、体验卡、benefits 和 settings 保持在同一 repeatable-read 快照。
func (r *Repository) queryVipUserSettings(ctx context.Context, querier interface {
	QueryRowContext(context.Context, string, ...any) *sql.Row
}, userID int64, lock bool) (ledger.VipUserSettings, error) {
	query := `
		SELECT app_code, user_id, room_entry_notice_enabled,
		       online_global_notice_enabled, hide_profile_data_enabled,
		       anonymous_profile_visit_enabled, leaderboard_invisible_enabled, updated_at_ms
		FROM user_vip_settings
		WHERE app_code = ? AND user_id = ?`
	if lock {
		query += ` FOR UPDATE`
	}
	var settings ledger.VipUserSettings
	if err := querier.QueryRowContext(ctx, query, appcode.FromContext(ctx), userID).Scan(
		&settings.AppCode, &settings.UserID, &settings.RoomEntryNoticeEnabled,
		&settings.OnlineGlobalNoticeEnabled, &settings.HideProfileDataEnabled,
		&settings.AnonymousProfileVisitEnabled, &settings.LeaderboardInvisibleEnabled,
		&settings.UpdatedAtMS,
	); err != nil {
		if errors.Is(err, sql.ErrNoRows) {
			return ledger.DefaultVipUserSettings(appcode.FromContext(ctx), userID), nil
		}
		return ledger.VipUserSettings{}, err
	}
	return settings, nil
}
