// Package device implements user-service push token persistence.
package device

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

	"hyapp/pkg/appcode"
	userdomain "hyapp/services/user-service/internal/domain/user"
)

// Repository stores App push tokens in MySQL.
type Repository struct {
	db *sql.DB
}

// New creates a device repository over the shared user-service connection pool.
func New(db *sql.DB) *Repository {
	return &Repository{db: db}
}

// BindPushToken binds a push token to the current user/device and removes stale ownership of the same token.
func (r *Repository) BindPushToken(ctx context.Context, command userdomain.PushTokenCommand) error {
	appCode := normalizedAppCode(ctx, command.AppCode)
	tx, err := r.db.BeginTx(ctx, nil)
	if err != nil {
		return err
	}

	// 同一个系统 push token 只能投递一次；重新登录其他账号时删除旧归属，避免离线消息双发。
	if _, err := tx.ExecContext(ctx,
		`DELETE FROM user_push_tokens
		 WHERE app_code = ? AND provider = ? AND push_token = ?
		   AND NOT (user_id = ? AND device_id = ?)`,
		appCode,
		command.Provider,
		command.PushToken,
		command.UserID,
		command.DeviceID,
	); err != nil {
		_ = tx.Rollback()
		return err
	}

	if _, err := tx.ExecContext(ctx,
		`INSERT INTO user_push_tokens (
		   app_code, user_id, device_id, push_token, provider, platform, app_version, language, timezone,
		   status, bound_at_ms, updated_at_ms, last_request_id
		 ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
		 ON DUPLICATE KEY UPDATE
		   push_token = VALUES(push_token),
		   provider = VALUES(provider),
		   platform = VALUES(platform),
		   app_version = VALUES(app_version),
		   language = VALUES(language),
		   timezone = VALUES(timezone),
		   status = VALUES(status),
		   updated_at_ms = VALUES(updated_at_ms),
		   last_request_id = VALUES(last_request_id)`,
		appCode,
		command.UserID,
		command.DeviceID,
		command.PushToken,
		command.Provider,
		command.Platform,
		command.AppVersion,
		command.Language,
		command.Timezone,
		userdomain.PushTokenStatusActive,
		command.UpdatedAtMs,
		command.UpdatedAtMs,
		command.RequestID,
	); err != nil {
		_ = tx.Rollback()
		return err
	}

	return tx.Commit()
}

// DeletePushToken marks one user's device token inactive.
func (r *Repository) DeletePushToken(ctx context.Context, command userdomain.DeletePushTokenCommand) (bool, error) {
	appCode := normalizedAppCode(ctx, command.AppCode)
	query := `UPDATE user_push_tokens
		SET status = ?, updated_at_ms = ?, last_request_id = ?
		WHERE app_code = ? AND user_id = ? AND device_id = ? AND status = ?`
	args := []any{
		userdomain.PushTokenStatusInactive,
		command.UpdatedAtMs,
		command.RequestID,
		appCode,
		command.UserID,
		command.DeviceID,
		userdomain.PushTokenStatusActive,
	}
	if strings.TrimSpace(command.PushToken) != "" {
		query += ` AND push_token = ?`
		args = append(args, command.PushToken)
	}

	result, err := r.db.ExecContext(ctx, query, args...)
	if err != nil {
		return false, err
	}
	affected, err := result.RowsAffected()
	if err != nil {
		return false, err
	}
	return affected > 0, nil
}

func normalizedAppCode(ctx context.Context, value string) string {
	value = strings.TrimSpace(value)
	if value == "" {
		return appcode.FromContext(ctx)
	}
	return appcode.Normalize(value)
}
