/**
 * cachedGiteaSprint 快取層測試（總管 #36 審查要求的證據；fix/console-truth-audit 起
 * 同一輪快取多帶任務板 board——「等你的事」與「今日完成」共用同一次 Gitea fetch）。
 *
 * 跑在 vitest-pool-workers（workerd）裡，caches.default 是真的 Cache API——
 * 不是 mock cache 物件；fetcher 用注入的假函式計數，證明：
 *   1. 第一次 miss（打 fetcher）→ 90s 內第二次 hit（fetcher 不再被打），board 一起回放
 *   2. hit 回放時 age 隨牆鐘推進（不回放停走的時間）
 *   3. fetcher 失敗（回 null）不快取——下次仍會重試，不把抖動放大成 90 秒盲區
 *   4. 缺 GITEA_BASE_URL/GITEA_TOKEN 直接 null（快取層不啟動）
 */
import { describe, it, expect } from 'vitest';
import { cachedGiteaSprint } from '../src/routes/console-dashboard';
import type { SprintSnapshot } from '../src/lib/console-dashboard-model';
import type { Bindings } from '../src/types';

function fakeEnv(repo: string): Bindings {
  return {
    GITEA_BASE_URL: 'https://git.example.test',
    GITEA_TOKEN: 'test-token',
    GITEA_SPRINT_REPO: repo, // 每測試一個 repo → cache key 隔離
    GITEA_SPRINT_DIR: 'system-dev/docs/3-specs/autonomy-dispatch',
  } as unknown as Bindings;
}

function snapshot(): SprintSnapshot {
  return {
    waiting: {
      items: [{ title: 'mira .env 歷史憑證外洩', id: '13', urgency: '🔴', sprint: 'sprint-2026-07a.md' }],
      source: 'gitea_sprint',
      updated_ago_minutes: 10,
      stale: false,
      sprint_files: ['sprint-2026-07b.md', 'sprint-2026-07a.md'],
    },
    board: [
      { title: 'T5-graph（Graph 查詢 workflow 化）', status: 'todo', completed_days: [], sprint: 'sprint-2026-07b.md' },
      { title: 'T-kb2 07-07', status: 'done', completed_days: ['2026-07-05'], sprint: 'sprint-2026-07a.md' },
    ],
  };
}

/** 收集 waitUntil 的 promise，flush 後 cache.put 才保證落地 */
function collector() {
  const pending: Promise<unknown>[] = [];
  return { waitUntil: (p: Promise<unknown>) => pending.push(p), flush: () => Promise.all(pending) };
}

describe('cachedGiteaSprint — Gitea 讀取快取（TTL 90s，waiting + board 同輪）', () => {
  it('第一次 miss 打 fetcher；90s 內第二次 hit、fetcher 不再被打，board 一起回放', async () => {
    const env = fakeEnv('Leo/cache-test-1');
    let calls = 0;
    const fetcher = async () => {
      calls++;
      return snapshot();
    };
    const c = collector();
    const t0 = Date.now();

    const r1 = await cachedGiteaSprint(env, t0, c.waitUntil, fetcher);
    expect(r1?.cache).toBe('miss');
    expect(calls).toBe(1);
    await c.flush(); // cache.put 落地

    const r2 = await cachedGiteaSprint(env, t0 + 60_000, c.waitUntil, fetcher);
    expect(r2?.cache).toBe('hit'); // ← 快取生效的客觀證據
    expect(calls).toBe(1); // fetcher 沒有被第二次呼叫＝Gitea 沒被打
    expect(r2?.waiting.items[0].id).toBe('13');
    // 任務板同輪回放：存的是原始 completed_days（「今天完成幾件」由請求當下算，跨午夜不冒領）
    expect(r2?.board?.length).toBe(2);
    expect(r2?.board?.[1].completed_days).toEqual(['2026-07-05']);
  });

  it('hit 回放時 age 隨牆鐘推進（存 10 分鐘前、過 60s 讀 → 11 分鐘前），不停走', async () => {
    const env = fakeEnv('Leo/cache-test-2');
    const c = collector();
    const t0 = Date.now();
    await cachedGiteaSprint(env, t0, c.waitUntil, async () => snapshot());
    await c.flush();
    const r = await cachedGiteaSprint(env, t0 + 60_000, c.waitUntil, async () => snapshot());
    expect(r?.cache).toBe('hit');
    expect(r?.waiting.updated_ago_minutes).toBe(11); // 10 + 1 分鐘 drift
  });

  it('fetcher 失敗（null）不快取：下一次仍重打 fetcher', async () => {
    const env = fakeEnv('Leo/cache-test-3');
    const c = collector();
    let calls = 0;
    const failing = async () => {
      calls++;
      return null;
    };
    expect(await cachedGiteaSprint(env, Date.now(), c.waitUntil, failing)).toBeNull();
    await c.flush();
    expect(await cachedGiteaSprint(env, Date.now(), c.waitUntil, failing)).toBeNull();
    expect(calls).toBe(2); // 失敗沒有被 90 秒快取住
  });

  it('缺 GITEA_BASE_URL/GITEA_TOKEN → null（快取層不啟動，caller 走 fallback）', async () => {
    const env = { GITEA_SPRINT_REPO: 'Leo/x' } as unknown as Bindings;
    let calls = 0;
    const fetcher = async () => {
      calls++;
      return snapshot();
    };
    const c = collector();
    expect(await cachedGiteaSprint(env, Date.now(), c.waitUntil, fetcher)).toBeNull();
    expect(calls).toBe(0);
  });
});
