// portal-auth P2 附帶修復回歸測試（design §2.2）：
// updateRecord 的「grow 新 slot」路徑建 entry 時，必須帶 record 既有 owner_id。
// 修前：createEntry(db, { content, entry_type: 'value' }) → owner_id=NULL 孤兒 entry，
// owner-scoped 查詢（searchByTemplate JOIN entries.owner_id / searchEntries owner filter）看不到該 slot 值。
// 測法沿 library-filter.test.ts 慣例：fake D1 按 SQL 形狀路由回應＋捕捉 INSERT 參數
// （不解讀 SQL 語意——真語意由本機 miniflare 端到端驗證，PR 附證據）。
import { describe, it, expect } from 'vitest';
import { updateRecord } from '../src/actions/record-crud';

interface Captured { sql: string; params: unknown[] }

/**
 * 可路由 fake D1：
 *  - entry_values JOIN entries 查詢 → 回既有 slot rows（含 owner_id）
 *  - templates 查詢 → 回 template（slots_json 含既有 + 可 grow 的 slot）
 *  - INSERT INTO entries → 捕捉參數（本測試的斷言目標）
 *  - SELECT * FROM entries WHERE id → 回假 entry（createEntry 的 insert 後回讀）
 *  - 其餘（UPDATE / INSERT entry_values / getRecord SELECT）→ 空殼
 */
function makeRoutedDB(recordOwnerId: string | null, captured: Captured[]) {
  const prepare = (sql: string) => {
    const rec: Captured = { sql, params: [] };
    captured.push(rec);
    const stmt = {
      bind(...args: unknown[]) { rec.params = args; return stmt; },
      async all<T>() {
        if (sql.includes('FROM entry_values ev JOIN entries e')) {
          return {
            results: [
              { slot_name: 'email', entry_id: 'e_existing', template_id: 'tpl_pu', owner_id: recordOwnerId },
            ] as unknown as T[],
          };
        }
        if (sql.includes('FROM entry_values ev JOIN entries e ON ev.entry_id = e.id')) {
          return { results: [] as T[] };
        }
        return { results: [] as T[] };
      },
      async first<T>() {
        if (sql.includes('FROM templates')) {
          return {
            id: 'tpl_pu', name: 'portal_user', description: null,
            slots_json: JSON.stringify(['email', 'status']), created_by: 'system',
          } as unknown as T;
        }
        if (sql.startsWith('SELECT * FROM entries WHERE id')) {
          return {
            id: 'e_new', content: 'active', entry_type: 'value', owner_id: recordOwnerId,
            parent_id: null, page_name: null, refs_json: '[]', tags_json: '[]', task_status: null,
            content_hash: null, is_embedded: 0, confidence: null, metadata_json: null,
            created_at: 1, updated_at: 1,
          } as unknown as T;
        }
        return null as unknown as T;
      },
      async run() { return { success: true }; },
    };
    return stmt;
  };
  return { prepare } as unknown as D1Database;
}

describe('updateRecord grow-slot 路徑帶 owner_id（design §2.2 回歸）', () => {
  it('record 有 owner_id → grow 出的新 entry INSERT 帶同一 owner_id（修前為 null）', async () => {
    const captured: Captured[] = [];
    const db = makeRoutedDB('leo::portal', captured);
    // 'status' slot 在 template 裡但 record 尚無 → 走 grow 路徑
    await updateRecord(db, 'rec_1', { status: 'active' });

    const insertEntry = captured.find((c) => c.sql.includes('INSERT INTO entries'));
    expect(insertEntry, 'grow 路徑應 INSERT 新 entry').toBeTruthy();
    // entry-crud.ts createEntry 的 INSERT 欄位序：id, content, entry_type, owner_id, ...
    expect(insertEntry!.params[1]).toBe('active');       // content
    expect(insertEntry!.params[2]).toBe('value');        // entry_type
    expect(insertEntry!.params[3]).toBe('leo::portal');  // owner_id ← 本修復的斷言核心
  });

  it('record 本就無 owner_id（全域資料）→ grow 出的 entry owner_id 維持 null（行為不變）', async () => {
    const captured: Captured[] = [];
    const db = makeRoutedDB(null, captured);
    await updateRecord(db, 'rec_1', { status: 'active' });

    const insertEntry = captured.find((c) => c.sql.includes('INSERT INTO entries'));
    expect(insertEntry).toBeTruthy();
    expect(insertEntry!.params[3]).toBeNull();
  });

  it('既有 slot 更新（非 grow）→ 走 UPDATE entries，不 INSERT 新 entry', async () => {
    const captured: Captured[] = [];
    const db = makeRoutedDB('leo::portal', captured);
    await updateRecord(db, 'rec_1', { email: 'new@example.com' });

    expect(captured.some((c) => c.sql.includes('UPDATE entries SET content'))).toBe(true);
    expect(captured.some((c) => c.sql.includes('INSERT INTO entries'))).toBe(false);
  });
});
