package auth

import (
	"context"
	"database/sql"
	"strings"

	"hyapp/pkg/appcode"
	"hyapp/pkg/xerr"
	authdomain "hyapp/services/user-service/internal/domain/auth"
)

func (r *Repository) GetRegisterRiskConfig(ctx context.Context, appCode string) (authdomain.RegisterRiskConfig, error) {
	var config authdomain.RegisterRiskConfig
	err := r.db.QueryRowContext(ctx, `
		SELECT app_code, max_accounts_per_device, updated_by_admin_id, created_at_ms, updated_at_ms
		FROM auth_risk_configs
		WHERE app_code = ?
	`, appcode.Normalize(appCode)).Scan(&config.AppCode, &config.MaxAccountsPerDevice, &config.UpdatedByAdminID, &config.CreatedAtMs, &config.UpdatedAtMs)
	if err == sql.ErrNoRows {
		return authdomain.RegisterRiskConfig{}, xerr.New(xerr.NotFound, "register risk config not found")
	}
	if err != nil {
		return authdomain.RegisterRiskConfig{}, err
	}

	return config, nil
}

func (r *Repository) UpsertRegisterRiskConfig(ctx context.Context, config authdomain.RegisterRiskConfig) (authdomain.RegisterRiskConfig, error) {
	appCode := appcode.Normalize(config.AppCode)
	if config.MaxAccountsPerDevice < 1 {
		return authdomain.RegisterRiskConfig{}, xerr.New(xerr.InvalidArgument, "max_accounts_per_device must be greater than 0")
	}
	_, err := r.db.ExecContext(ctx, `
		INSERT INTO auth_risk_configs (
		  app_code, max_accounts_per_device, updated_by_admin_id, created_at_ms, updated_at_ms
		) VALUES (?, ?, ?, ?, ?)
		ON DUPLICATE KEY UPDATE
		  max_accounts_per_device = VALUES(max_accounts_per_device),
		  updated_by_admin_id = VALUES(updated_by_admin_id),
		  updated_at_ms = VALUES(updated_at_ms)
	`, appCode, config.MaxAccountsPerDevice, config.UpdatedByAdminID, config.CreatedAtMs, config.UpdatedAtMs)
	if err != nil {
		return authdomain.RegisterRiskConfig{}, err
	}
	return r.GetRegisterRiskConfig(ctx, appCode)
}

func ensureRegisterDeviceQuotaTx(ctx context.Context, tx *sql.Tx, appCode string, deviceID string, maxAccountsPerDevice int32) error {
	appCode = appcode.Normalize(appCode)
	deviceID = strings.TrimSpace(deviceID)
	if deviceID == "" {
		return nil
	}
	if maxAccountsPerDevice < 1 {
		maxAccountsPerDevice = 1
	}
	rows, err := tx.QueryContext(ctx, `
		SELECT current_display_user_id
		FROM users
		WHERE app_code = ? AND register_device_id = ?
		ORDER BY created_at_ms ASC, user_id ASC
		FOR UPDATE
	`, appCode, deviceID)
	if err != nil {
		return err
	}
	defer rows.Close()

	var count int32
	displayUserIDs := make([]string, 0)
	for rows.Next() {
		// FOR UPDATE 锁住同设备的已有用户行；读完后再判断，确保等待中的并发事务能看到最新提交结果。
		var displayUserID string
		if err := rows.Scan(&displayUserID); err != nil {
			return err
		}
		count++
		displayUserIDs = append(displayUserIDs, displayUserID)
	}
	if err := rows.Err(); err != nil {
		return err
	}
	if count >= maxAccountsPerDevice {
		return xerr.New(xerr.DeviceAlreadyRegistered, xerr.DeviceAlreadyRegisteredMessage(int(count), displayUserIDs))
	}
	return nil
}
