-- name: UpsertClipboard :one
-- New rows start version 1; conflicts bump version by 1 (per-user monotonic).
-- LWW: the DO UPDATE only runs when the new origin_ts is strictly greater than
-- the stored one, so a delayed / out-of-order older copy changes nothing and
-- RETURNING yields no row (the service reads sql.ErrNoRows as "stale, rejected").
INSERT INTO clipboard_state (user_id, content_type, content, source_device, updated_at, version, origin_ts)
VALUES (?, ?, ?, ?, ?, 1, ?)
ON CONFLICT (user_id) DO UPDATE SET
    content_type  = excluded.content_type,
    content       = excluded.content,
    source_device = excluded.source_device,
    updated_at    = excluded.updated_at,
    version       = clipboard_state.version + 1,
    origin_ts     = excluded.origin_ts
WHERE excluded.origin_ts > clipboard_state.origin_ts
RETURNING version;

-- name: GetClipboard :one
SELECT user_id, content_type, content, source_device, updated_at, version, origin_ts
FROM clipboard_state
WHERE user_id = ?;

-- name: ClearExpiredClipboards :execrows
-- 清除 updated_at 早于 cutoff 的剪贴板内容（content / source_device 置空），
-- 由 sweeper 周期调用实现短 TTL；保留行本身、仅抹掉内容。
UPDATE clipboard_state
SET content = NULL, source_device = NULL
WHERE content IS NOT NULL AND updated_at < ?;
