/**
 * Regression guard — EXIF orientation must be applied when generating derivatives.
 *
 * Phones do not rotate sensor data. They shoot in the sensor's native landscape and record
 * how the camera was held in an EXIF `Orientation` tag. `image`'s `decode()` returns the raw
 * pixels and ignores that tag, and the JPEG re-encode writes no EXIF at all — so every
 * portrait photo was stored SIDEWAYS in the 800px feed preview, the 2048px diashow display
 * and the keepsake, while "Original anzeigen" still rendered it upright (the original keeps
 * its tag). That asymmetry is why it reads as a viewer bug instead of a pipeline one.
 *
 * The fixture is 40x20 landscape pixels tagged Orientation=6 ("rotate 90° CW to display"),
 * so a correctly-processed derivative is PORTRAIT (20x40). Asserting on the aspect ratio
 * rather than the bytes keeps this robust across encoder changes.
 */
import { test, expect } from '../../fixtures/test';
import { uploadRaw } from '../../helpers/upload-client';
import { BASE } from '../../helpers/env';
import { readFileSync } from 'node:fs';
import { join } from 'node:path';

const EXIF_FIXTURE = join(process.cwd(), 'fixtures', 'media', 'portrait-exif6.jpg');

/**
 * Read a baseline/progressive JPEG's pixel dimensions from its SOF marker.
 * Avoids pulling an image dependency into the suite for one assertion.
 */
function jpegSize(buf: Buffer): { width: number; height: number } {
  let i = 2; // skip SOI
  while (i < buf.length) {
    if (buf[i] !== 0xff) {
      i++;
      continue;
    }
    const marker = buf[i + 1];
    // SOF0..SOF15, excluding DHT (c4), JPGA (c8) and DAC (cc)
    if (marker >= 0xc0 && marker <= 0xcf && marker !== 0xc4 && marker !== 0xc8 && marker !== 0xcc) {
      return { height: buf.readUInt16BE(i + 5), width: buf.readUInt16BE(i + 7) };
    }
    i += 2 + buf.readUInt16BE(i + 2);
  }
  throw new Error('no SOF marker found — not a JPEG?');
}

test.describe('Upload — EXIF orientation', () => {
  test('a rotated photo is upright in the preview and the display derivative', async ({
    guest,
    db,
  }) => {
    const g = await guest('SidewaysShooter');

    const res = await uploadRaw(g.jwt, readFileSync(EXIF_FIXTURE), {
      filename: 'portrait-exif6.jpg',
      contentType: 'image/jpeg',
      caption: 'hochkant',
    });
    expect(res.status).toBe(201);
    const { id } = (await res.json()) as { id: string };

    // Sanity: the SOURCE really is stored landscape with the tag, otherwise this test
    // could pass against a pipeline that does nothing.
    const source = jpegSize(readFileSync(EXIF_FIXTURE));
    expect(source.width).toBeGreaterThan(source.height);

    await expect
      .poll(() => db.compressionStatus(id), { timeout: 30_000, intervals: [250] })
      .toBe('done');

    for (const variant of ['preview', 'display'] as const) {
      const r = await fetch(`${BASE}/api/v1/upload/${id}/${variant}`, {
        headers: { Authorization: `Bearer ${g.jwt}` },
      });
      expect(r.status, `${variant} must be served`).toBe(200);
      const { width, height } = jpegSize(Buffer.from(await r.arrayBuffer()));
      expect(
        height,
        `${variant} must be portrait (${width}x${height}) — EXIF orientation was not applied`
      ).toBeGreaterThan(width);
    }
  });
});
