/**
 * Regression guard — a rejected upload must tell the user something.
 *
 * `UploadQueue.svelte` was 162 lines of complete, working UI — the only renderer of an
 * item's error text, the only "Erneut" retry button, the only rate-limit countdown — and it
 * was never imported anywhere, so `retryItem`, `removeItem` and `clearCompleted` were all
 * unreachable at runtime. On a terminal rejection the store dropped the blob, wrote a clear
 * German reason into `entry.error` with the comment "so the UI shows a clear reason", and
 * there was no such UI.
 *
 * Meanwhile the FAB badge counted only pending/uploading, so a rejected photo decremented it
 * exactly as if it had succeeded. Net effect: the photo silently vanished — no toast, no
 * queue row, no error text, and it never appeared in the feed.
 */
import { test, expect } from '../../fixtures/test';
import { skipIfNoIdbBlobs } from '../../helpers/webkit';
import { FeedPage, UploadSheet } from '../../page-objects';
import { join } from 'node:path';

const SAMPLE_JPG = join(process.cwd(), 'fixtures', 'media', 'sample.jpg');

test.describe('Upload — a rejected upload is surfaced', () => {
  test('a terminally rejected upload toasts, and stays visible in the queue', async ({
    page,
    api,
    host,
    guest,
    signIn,
    browserName,
  }) => {
    skipIfNoIdbBlobs(browserName);
    const g = await guest('RejectedUploader');
    await signIn(page, g);

    const feed = new FeedPage(page);
    const sheet = new UploadSheet(page);
    await feed.openUploadSheet();
    await sheet.stageFiles([SAMPLE_JPG]);
    await sheet.captionInput.waitFor({ state: 'visible', timeout: 10_000 });

    // Ban the uploader between staging and sending, so the POST comes back 403 — a
    // terminal 4xx the server will keep rejecting, which is the path that purges the blob.
    await api.banUser(host.jwt, g.userId);

    await sheet.submit();

    // 1. The user is told, wherever they are (the flow lands them on /feed).
    const toast = page.getByRole('region', { name: 'Benachrichtigungen' });
    await expect(toast).toContainText(/sample\.jpg/i, { timeout: 15_000 });

    // 2. The queue row survives with its reason and is reachable on /upload — this is what
    //    the orphaned component made impossible.
    await page.goto('/upload');
    const queue = page.getByText('Upload-Warteschlange');
    await expect(queue, 'the upload queue must be rendered somewhere').toBeVisible({
      timeout: 10_000,
    });
    // The server's reason ("Du bist gesperrt.") must render — it had no UI at all before.
    await expect(page.getByText('Du bist gesperrt.')).toBeVisible();
    // The chip reads "Fehler", NOT "Gesperrt", and that is the fix rather than a regression.
    // A ban used to come back as a generic `forbidden`, which purged the blob and moved the row
    // to `blocked` — a terminal state with no retry button. So an unban restored everything
    // except the photo that was actually in flight, which is the one the guest cares about.
    // It is now a distinct `user_banned` code that PARKS the row (status `error`, blob kept,
    // `parkedFor: 'unban'`) and resumes it when `user-shown` arrives.
    await expect(page.getByText('Gesperrt', { exact: true })).toHaveCount(0);
    // Positively, not just negatively: a chip that rendered empty would satisfy the line above.
    await expect(page.getByText('Fehler', { exact: true }).first()).toBeVisible();

    // 3. The badge must not read as success. It counted only pending/uploading before, so a
    //    rejected item dropped it to 0 — indistinguishable from a completed upload.
    //    The row is now parked (`error` + `parkedFor: 'unban'`) rather than terminally `blocked`,
    //    so it is the parked count that must be exactly one — and critically the blob must still
    //    be there, since that is what an unban replays.
    await expect
      .poll(
        () =>
          page.evaluate(async () => {
            return new Promise<number>((resolve, reject) => {
              const req = indexedDB.open('eventsnap-uploads', 3);
              req.onerror = () => reject(req.error);
              req.onsuccess = () => {
                const tx = req.result.transaction('queue', 'readonly');
                const all = tx.objectStore('queue').getAll();
                all.onsuccess = () =>
                  resolve(
                    all.result.filter(
                      (r: { status: string; parkedFor?: string; blob?: Blob }) =>
                        r.status === 'error' && r.parkedFor === 'unban' && !!r.blob
                    ).length
                  );
                all.onerror = () => reject(all.error);
              };
            });
          }),
        { timeout: 10_000 }
      )
      .toBe(1);
  });
});
