/**
 * POST /console/triage-check route 測試（分流台勾掉/還原，leo 2026-07-08 拍板）
 *
 * 驗證 IO 接線（改寫邏輯本身在 console-triage-model.test.ts 的 applyTriageCheck 純函式測試）：
 *   1. session 閘：無/壞 Bearer → 401（沿用 triage-data 同款 validateConsoleSession）
 *   2. 整串回寫：先 GET 原 entry → PATCH content 帶完整 JSON（text/marker/owner_tier 等
 *      原樣保留 + status:done + checked_via:console + checked_at）——防 PATCH 蓋掉別的欄位
 *   3. 邊界：owner_id 非 console 租戶 → 404 不 PATCH；entry_type 非 todo/inbox → 400 不 PATCH
 *   4. restore：status 回 new、checked_via/checked_at 移除
 *
 * KBDB 打 fetchMock 假 host（wrangler.test.toml KBDB_BASE_URL=https://kbdb.test）＋
 * disableNetConnect——測試絕不外連（更不會碰官方 uncle6 fallback）。
 */
import { SELF, env, fetchMock } from 'cloudflare:test';
import { beforeAll, beforeEach, afterEach, describe, it, expect } from 'vitest';

const TOKEN = 'test-session-token';
const AUTH = { Authorization: `Bearer ${TOKEN}` };

beforeAll(() => {
  fetchMock.activate();
  fetchMock.disableNetConnect();
});
afterEach(() => fetchMock.assertNoPendingInterceptors());

beforeEach(async () => {
  // isolatedStorage：每個測試自己 seed session（console-auth.ts 的 key 規約 console_sess:<token>）
  await env.SESSIONS_KV.put(`console_sess:${TOKEN}`, JSON.stringify({ created_at: Date.now() }));
});

function post(body: unknown, headers: Record<string, string> = AUTH) {
  return SELF.fetch('http://localhost/console/triage-check', {
    method: 'POST',
    headers: { 'Content-Type': 'application/json', ...headers },
    body: JSON.stringify(body),
  });
}

/** mock GET /entries/:id（KBDB base 回應形狀 {success, entry}） */
function mockGet(id: string, entry: Record<string, unknown>) {
  fetchMock
    .get('https://kbdb.test')
    .intercept({ path: `/entries/${id}`, method: 'GET' })
    .reply(200, { success: true, entry });
}

describe('POST /console/triage-check — session 閘', () => {
  it('無 Bearer → 401，不碰 KBDB', async () => {
    const res = await post({ entry_id: 'e1' }, {});
    expect(res.status).toBe(401);
  });
  it('壞 token → 401', async () => {
    const res = await post({ entry_id: 'e1' }, { Authorization: 'Bearer wrong-token' });
    expect(res.status).toBe(401);
  });
  it('entry_id 缺 → 400', async () => {
    const res = await post({});
    expect(res.status).toBe(400);
  });
});

describe('POST /console/triage-check — check（勾掉＝console 端終局銷案）', () => {
  it('先 GET 原 entry 再整串回寫：其餘欄位原樣保留 + status/checked_via/checked_at', async () => {
    mockGet('e1', {
      id: 'e1',
      owner_id: 'leo',
      entry_type: 'todo',
      content: JSON.stringify({ text: '把憑證輪替', marker: 'TODO', owner_tier: 'ai', project: 'Arcrun', source: 'notes', status: 'new' }),
    });
    let patched = '';
    fetchMock
      .get('https://kbdb.test')
      .intercept({ path: '/entries/e1', method: 'PATCH' })
      .reply(200, (opts) => {
        patched = String(opts.body);
        return { success: true };
      });

    const res = await post({ entry_id: 'e1' });
    const data = (await res.json()) as Record<string, unknown>;
    expect(res.status).toBe(200);
    expect(data.success).toBe(true);
    expect(data.status).toBe('done');

    const sent = JSON.parse(patched) as { content: string };
    const content = JSON.parse(sent.content) as Record<string, unknown>;
    expect(content.text).toBe('把憑證輪替');
    expect(content.marker).toBe('TODO');
    expect(content.owner_tier).toBe('ai');
    expect(content.project).toBe('Arcrun');
    expect(content.source).toBe('notes');
    expect(content.status).toBe('done');
    expect(content.checked_via).toBe('console'); // 終局標記：萃取端看到絕不復活
    expect(typeof content.checked_at).toBe('string');
  });

  it('owner_id 非 console 租戶 → 404（不洩漏他租戶存在性），不發 PATCH', async () => {
    mockGet('e2', { id: 'e2', owner_id: 'someone-else', entry_type: 'todo', content: '{}' });
    const res = await post({ entry_id: 'e2' });
    expect(res.status).toBe(404);
  });

  it('entry_type 非 todo/inbox → 400（不是泛用 entry 改寫器），不發 PATCH', async () => {
    mockGet('e3', { id: 'e3', owner_id: 'leo', entry_type: 'wiki_card', content: '{}' });
    const res = await post({ entry_id: 'e3' });
    expect(res.status).toBe(400);
  });

  it('KBDB 找不到 entry → 404', async () => {
    fetchMock
      .get('https://kbdb.test')
      .intercept({ path: '/entries/gone', method: 'GET' })
      .reply(404, { success: false, error: 'not found' });
    const res = await post({ entry_id: 'gone' });
    expect(res.status).toBe(404);
  });
});

describe('POST /console/triage-check — restore（誤勾救濟）', () => {
  it('status 回 new、checked_via/checked_at 移除、其餘欄位保留（inbox 也適用）', async () => {
    mockGet('e4', {
      id: 'e4',
      owner_id: 'leo',
      entry_type: 'inbox',
      content: JSON.stringify({ text: '誤勾的', from: 'leo', status: 'done', checked_via: 'console', checked_at: '2026-07-08T11:00:00.000Z' }),
    });
    let patched = '';
    fetchMock
      .get('https://kbdb.test')
      .intercept({ path: '/entries/e4', method: 'PATCH' })
      .reply(200, (opts) => {
        patched = String(opts.body);
        return { success: true };
      });

    const res = await post({ entry_id: 'e4', action: 'restore' });
    const data = (await res.json()) as Record<string, unknown>;
    expect(res.status).toBe(200);
    expect(data.status).toBe('new');

    const content = JSON.parse((JSON.parse(patched) as { content: string }).content) as Record<string, unknown>;
    expect(content).toEqual({ text: '誤勾的', from: 'leo', status: 'new' });
  });
});
