/**
 * SET 50 — web runtime stale-bundle banner.
 */
import { describe, expect, it } from 'vitest';
import fs from 'node:fs';
import os from 'node:os';
import path from 'node:path';
import {
  parseWebVersion,
  shouldShowStaleBundleBanner,
} from '../web/src/staleBundle';
import { emitWebVersion } from '../scripts/emit-web-version';

describe('SET 50 shouldShowStaleBundleBanner', () => {
  it('mismatched remote → banner; matching → none', () => {
    expect(shouldShowStaleBundleBanner({
      selfBuildId: 'a',
      remoteBuildId: 'b',
      dismissedRemoteBuildId: null,
    })).toBe(true);
    expect(shouldShowStaleBundleBanner({
      selfBuildId: 'a',
      remoteBuildId: 'a',
      dismissedRemoteBuildId: null,
    })).toBe(false);
  });

  it('dismiss-until-next-mismatch: same remote stays quiet; newer remote returns', () => {
    expect(shouldShowStaleBundleBanner({
      selfBuildId: 'a',
      remoteBuildId: 'b',
      dismissedRemoteBuildId: 'b',
    })).toBe(false);
    expect(shouldShowStaleBundleBanner({
      selfBuildId: 'a',
      remoteBuildId: 'c',
      dismissedRemoteBuildId: 'b',
    })).toBe(true);
  });

  it('null self/remote → no banner', () => {
    expect(shouldShowStaleBundleBanner({
      selfBuildId: null,
      remoteBuildId: 'b',
      dismissedRemoteBuildId: null,
    })).toBe(false);
    expect(shouldShowStaleBundleBanner({
      selfBuildId: 'a',
      remoteBuildId: null,
      dismissedRemoteBuildId: null,
    })).toBe(false);
  });
});

describe('SET 50 parseWebVersion + emit', () => {
  it('rejects garbage', () => {
    expect(parseWebVersion(null)).toBeNull();
    expect(parseWebVersion({})).toBeNull();
    expect(parseWebVersion({ buildId: '' })).toBeNull();
    expect(parseWebVersion({ buildId: 'x', generatedAt: 't' }))
      .toEqual({ buildId: 'x', generatedAt: 't' });
  });

  it('emitWebVersion writes {buildId, generatedAt} shape', () => {
    const dir = fs.mkdtempSync(path.join(os.tmpdir(), 'rha-ver-'));
    const outJson = path.join(dir, 'version.json');
    const outTs = path.join(dir, 'webVersion.ts');
    const v = emitWebVersion({ outJson, outTs });
    const parsed = JSON.parse(fs.readFileSync(outJson, 'utf8')) as { buildId: string; generatedAt: string };
    expect(parsed.buildId).toBe(v.buildId);
    expect(parsed.generatedAt).toBe(v.generatedAt);
    const ts = fs.readFileSync(outTs, 'utf8');
    expect(ts).toContain(`WEB_BUILD_ID = ${JSON.stringify(v.buildId)}`);
  });
});

describe('SET 50 wiring contracts', () => {
  it('_headers includes /version.json must-revalidate', () => {
    const headers = fs.readFileSync(
      path.resolve(import.meta.dirname, '../web/public/_headers'),
      'utf8',
    );
    expect(headers).toMatch(/\/version\.json[\s\S]*?must-revalidate/);
  });

  it('App.tsx polls ~2min and shows the banner copy', () => {
    const src = fs.readFileSync(
      path.resolve(import.meta.dirname, '../web/src/App.tsx'),
      'utf8',
    );
    expect(src).toContain('STALE_BUNDLE_POLL_MS');
    expect(src).toContain('STALE_BUNDLE_BANNER');
    expect(src).toContain('shouldShowStaleBundleBanner');
    expect(src).toContain('/version.json');
    expect(src).toContain('stalebanner');
  });
});
