/**
 * credential-store-migration T6/T7（方案 A，D19）測試：auth-dispatcher 的
 * `resolveSecretsFromNewHome`——「查 D1 拿 secret_ref → secret_get(env[ref]) 取值 → 組 map」。
 *
 * 覆蓋 T6 主路徑 + T7 雙讀分流的判準：
 *   - D1 有 ref 且 secret 存在（新家有值）→ name 進 map（payload 會含 resolved_secrets[name]）
 *   - D1 無 ref（從未回填）→ name 缺席 map（讓 WASM fallback 舊 KV，T7）
 *   - D1 有 ref 但 secret 不存在（新家還沒寫值）→ secret_get 回 null → name 缺席（不放空字串！）
 *   - 取到值的 name 更新 last_used_at
 *
 * secret_get 的實作是 host function `env[ref]`（wasi-shim），故測試把 CRED_* 值放進傳入的
 * env 物件模擬「per-script secret 已注入」，用真實 D1 binding 存目錄（同 credentials.test.ts）。
 */
import { describe, it, expect, beforeEach } from 'vitest';
import { env } from 'cloudflare:test';
import type { Bindings } from '../src/types';
import { resolveSecretsFromNewHome } from '../src/actions/auth-dispatcher';

const API_KEY = 'test-tenant-t67';

async function insertRefRow(name: string, secretRef: string): Promise<void> {
  await env.CREDENTIALS_DB
    .prepare(
      `INSERT INTO credentials (api_key, name, service, sensitivity, secret_ref, created_at, last_used_at)
       VALUES (?, ?, ?, ?, ?, ?, NULL)`,
    )
    .bind(API_KEY, name, null, 'standard', secretRef, Math.floor(Date.now() / 1000))
    .run();
}

async function clearRows(): Promise<void> {
  await env.CREDENTIALS_DB.prepare(`DELETE FROM credentials WHERE api_key = ?`).bind(API_KEY).run();
}

/** 把 CRED_* secret 值疊到真實 env 上（模擬 per-script secret 已注入 runtime）。 */
function envWithSecrets(secrets: Record<string, string>): Bindings {
  return { ...(env as unknown as Record<string, unknown>), ...secrets } as unknown as Bindings;
}

describe('resolveSecretsFromNewHome (T6/T7 方案 A)', () => {
  beforeEach(clearRows);

  it('D1 有 ref 且新家有值 → name 進 map（T6 命中新家）', async () => {
    await insertRefRow('openai_key', 'CRED_OPENAI_KEY_ABCDEF01');
    const testEnv = envWithSecrets({ CRED_OPENAI_KEY_ABCDEF01: 'sk-live-123' });

    const resolved = await resolveSecretsFromNewHome(testEnv, API_KEY, ['openai_key']);

    expect(resolved).toEqual({ openai_key: 'sk-live-123' });
  });

  it('D1 無 ref（從未回填）→ name 缺席 map（走 WASM fallback 舊 KV，T7）', async () => {
    // D1 沒有這個 name 的 row
    const testEnv = envWithSecrets({ CRED_ANYTHING: 'unused' });

    const resolved = await resolveSecretsFromNewHome(testEnv, API_KEY, ['never_migrated']);

    expect(resolved).not.toHaveProperty('never_migrated');
    expect(resolved).toEqual({});
  });

  it('D1 有 ref 但新家沒值 → secret_get 回 null → name 缺席（不放空字串）', async () => {
    await insertRefRow('notion_token', 'CRED_NOTION_TOKEN_ABCDEF01');
    // env 裡沒有 CRED_NOTION_TOKEN_ABCDEF01 → env[ref] 為 undefined → secret_get 回 null
    const testEnv = envWithSecrets({});

    const resolved = await resolveSecretsFromNewHome(testEnv, API_KEY, ['notion_token']);

    // 關鍵：缺席，不是 { notion_token: '' }（空字串會讓 WASM 誤判命中用空值）
    expect(resolved).not.toHaveProperty('notion_token');
    expect(resolved).toEqual({});
  });

  it('混合：一把有值一把沒 ref → 只有有值的進 map', async () => {
    await insertRefRow('has_value', 'CRED_HAS_VALUE_ABCDEF01');
    const testEnv = envWithSecrets({ CRED_HAS_VALUE_ABCDEF01: 'the-value' });

    const resolved = await resolveSecretsFromNewHome(testEnv, API_KEY, ['has_value', 'no_ref']);

    expect(resolved).toEqual({ has_value: 'the-value' });
  });

  it('空 names → 空 map（零開銷）', async () => {
    const resolved = await resolveSecretsFromNewHome(envWithSecrets({}), API_KEY, []);
    expect(resolved).toEqual({});
  });

  it('取到值後更新 last_used_at（治理面）', async () => {
    await insertRefRow('used_key', 'CRED_USED_KEY_ABCDEF01');
    const before = await env.CREDENTIALS_DB
      .prepare(`SELECT last_used_at FROM credentials WHERE api_key = ? AND name = ?`)
      .bind(API_KEY, 'used_key')
      .first<{ last_used_at: number | null }>();
    expect(before?.last_used_at).toBeNull();

    const testEnv = envWithSecrets({ CRED_USED_KEY_ABCDEF01: 'v' });
    await resolveSecretsFromNewHome(testEnv, API_KEY, ['used_key']);

    const after = await env.CREDENTIALS_DB
      .prepare(`SELECT last_used_at FROM credentials WHERE api_key = ? AND name = ?`)
      .bind(API_KEY, 'used_key')
      .first<{ last_used_at: number | null }>();
    expect(typeof after?.last_used_at).toBe('number');
  });
});
