-- 0005_create_agents.up.sql
--
-- 個人設備管理走向 A'（ADR-018 Accepted）：新增 agents 實體（一條已配對的 tunnel 連線
-- = 一個 agent）；devices 語意轉為「一顆實體 USB」，掛 agent_id。
-- 對齊 .autoflow/04-architecture/personal-device-mgmt-data-model.md §2.3 / §2.6
-- 與 docs/autoflow/04-architecture/adr/adr-018-agent-device-model.md §5.1–§5.4，
-- 精確 SQL 規格見 .autoflow/04-architecture/migration-0005-spec.md。
--
-- 環境事實（與 0001–0003 相同，已驗證）：PostgreSQL 14.23，gen_random_uuid() 內建可直接用。
--
-- 不碰的東西（走向 A' 核心，避開破壞性 migration）：
--   - session_tokens.device_id 的 FK / NOT NULL：物理 schema 一字不動（改綁 representative
--     device 是純語意變更，不需 DDL）。
--   - uq_devices_owner_serial_active：現有 partial unique 已是 task 6 去重正確基礎，不動。

-- ── (1) agents 表 ────────────────────────────────────────────────────────────
CREATE TABLE agents (
    id             UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    owner_user_id  UUID NOT NULL REFERENCES users(id),
    name           TEXT NOT NULL DEFAULT 'local-agent',
    platform       TEXT,                                 -- darwin/windows/linux（agent 上報，可空）
    agent_version  TEXT,                                 -- agent 版本（可空）
    last_paired_at TIMESTAMPTZ,                          -- 最近一次配對完成時間
    created_at     TIMESTAMPTZ NOT NULL DEFAULT now(),
    updated_at     TIMESTAMPTZ NOT NULL DEFAULT now(),
    deleted_at     TIMESTAMPTZ
);

-- owner-scoped active index（List agents by owner；沿用 0002 partial 慣例）。
CREATE INDEX idx_agents_owner_active ON agents (owner_user_id) WHERE deleted_at IS NULL;

-- ── (2) devices 加欄位（語意轉為「實體 USB」）────────────────────────────────
-- 全部 nullable（或有 DEFAULT），對既有 row 零破壞：既有 device 加欄後 agent_id=NULL、
-- registered_at=NULL、is_representative=false，語意 = 「尚未歸入 agent 的舊資料」，
-- 由 (3) data migration 補齊。
ALTER TABLE devices ADD COLUMN agent_id              UUID REFERENCES agents(id);    -- NULL=代表 device/舊資料
ALTER TABLE devices ADD COLUMN agent_local_device_id TEXT;                          -- local agent 合成 id（kl520-0），路由除錯輔助
ALTER TABLE devices ADD COLUMN registered_at         TIMESTAMPTZ;                   -- NULL=未註冊（註冊軸）
ALTER TABLE devices ADD COLUMN is_representative     BOOLEAN NOT NULL DEFAULT false;-- true=agent 佔位/代表 device（非真 USB）

-- agent_id 反查（List devices by agent；只索引未刪除）。
CREATE INDEX idx_devices_agent_active ON devices (agent_id)      WHERE deleted_at IS NULL;
-- 註冊軸 filter（task 3 黃色「未註冊」/ task 4 filter「只看未註冊」；只索引未刪除）。
CREATE INDEX idx_devices_registered   ON devices (registered_at) WHERE deleted_at IS NULL;

-- ── (3) data migration：既有 device（皆 serial=NULL 的連線佔位）→ 1:1 建 agent + 標 representative ──
-- 純 SQL 兩句對應（本 runner 是 golang-migrate iofs，只跑 SQL，無 Go 逐筆 hook）。
--
-- 決定性推導方案（agent.id := device.id）：讓每筆現有 device 產生的 agent 用「與 device 相同
-- 的 UUID」。agents 與 devices 主鍵空間獨立（不同表），沿用同一 UUID 值不衝突；且讓 3b 的
-- 回填變成「device.agent_id := device.id」的自我對應，一句 UPDATE 完成、無關聯歧義、天然冪等。
-- 取捨與替代方案見 migration-0005-spec.md §2.1 / §5 R-A（使用者已裁決採此方案）。

--   3a. 對每筆現有未刪除 device，各建一筆 agent（id 沿用來源 device.id）。
INSERT INTO agents (id, owner_user_id, name, last_paired_at, created_at, updated_at)
    SELECT d.id, d.owner_user_id, 'local-agent', d.paired_at, d.created_at, now()
    FROM devices d
    WHERE d.deleted_at IS NULL;

--   3b. 回填：每筆現有 device 綁到「用它自己 id 建出來的 agent」，並標記為 representative。
--       （現有 device 本來就是「連線佔位」而非真 USB，serial 皆 NULL，故全部 is_representative=true。）
UPDATE devices d
    SET agent_id          = d.id,     -- 對應 3a：agent.id 就是 device.id
        is_representative = true,
        updated_at        = now()
    WHERE d.deleted_at IS NULL
      AND d.agent_id IS NULL;         -- 冪等保護：已綁過的不重綁

-- 註：已 soft-deleted 的舊 device 不建 agent、不回填（它們已退出使用；未來若復活走正常 exchange 流程）。
--     真實 USB device 在本次遷移後「還不存在」——等 agent 下次 exchange 帶 serial 上來（WP-0）
--     或 task 5 註冊時才建立，這符合「舊資料無序號」的事實（ADR-018 §4.3）。
