/**
 * Regression guard — a host must be able to remove a guest's content FROM THE UI.
 *
 * `DELETE /host/upload/{id}` and `DELETE /host/comment/{id}` were fully implemented,
 * transactional, SSE-broadcasting, audit-logged — and had zero frontend callers. The feed's
 * context sheet offered "Löschen" only when `target.user_id === myUserId`, so the only lever
 * a host actually had against an unwanted photo was banning the uploader. That is both
 * disproportionate and ineffective: a ban doesn't retract what was already posted, and
 * because the ban check runs BEFORE the ownership check on the guest delete route, banning
 * the author makes their abusive comment permanently undeletable by them too.
 *
 * The API side was already covered (04-host/moderation). What was missing is the wiring,
 * so these tests drive the real UI.
 */
import { test, expect } from '../../fixtures/test';
import { seedUpload, seedComment } from '../../helpers/seed';
import { BASE } from '../../helpers/env';

test.describe('Host — moderation from the UI', () => {
  test("a host removes a guest's photo via the feed context sheet", async ({
    page,
    host,
    guest,
    signIn,
  }) => {
    const g = await guest('PhotoOffender');
    const uploadId = await seedUpload(g.jwt);

    await signIn(page, host);
    await page.goto('/feed');

    const card = page.locator('article').filter({ hasText: g.displayName }).first();
    await expect(card).toBeVisible({ timeout: 15_000 });

    await card.getByRole('button', { name: 'Mehr Aktionen' }).click();
    const remove = page.getByRole('button', { name: /beitrag entfernen/i });
    await expect(remove, 'a host must be offered a removal action on a guest post').toBeVisible();
    await remove.click();

    const sheet = page.getByTestId('confirm-sheet');
    await expect(sheet).toBeVisible();
    // Moderation copy, not "delete my post" copy.
    await expect(sheet).toContainText(/beitrag entfernen/i);
    await page.getByTestId('confirm-sheet-confirm').click();

    await expect(card).not.toBeVisible({ timeout: 10_000 });

    // And it is really gone server-side, not just dropped from the local list.
    const res = await fetch(`${BASE}/api/v1/feed`, {
      headers: { Authorization: `Bearer ${host.jwt}` },
    });
    const body = await res.json();
    expect(body.uploads.some((u: { id: string }) => u.id === uploadId)).toBe(false);
  });

  test('a guest is NOT offered any delete action on someone else’s post', async ({
    page,
    guest,
    signIn,
  }) => {
    // The mirror that makes the test above meaningful: if this affordance rendered for
    // everyone, the host test would still pass on a build that shipped moderation to guests.
    const author = await guest('SomeAuthor');
    await seedUpload(author.jwt);
    const viewer = await guest('NosyViewer');

    await signIn(page, viewer);
    await page.goto('/feed');

    const card = page.locator('article').filter({ hasText: author.displayName }).first();
    await expect(card).toBeVisible({ timeout: 15_000 });
    await card.getByRole('button', { name: 'Mehr Aktionen' }).click();

    await expect(page.getByRole('button', { name: /beitrag entfernen/i })).toHaveCount(0);
    await expect(page.getByRole('button', { name: /^löschen$/i })).toHaveCount(0);
  });

  test('a promoted guest gets host powers without signing out and back in', async ({
    page,
    api,
    adminToken,
    guest,
    signIn,
  }) => {
    // The JWT is never reissued — the backend slides the session row forward and treats the
    // DB row as authoritative. So the token of a promoted guest still claims `role: guest`
    // for up to 30 days. The UI read that frozen claim, which meant a guest promoted at the
    // party saw no Host-Dashboard and no moderation actions until they signed out and back
    // in — while `/me/context` had been handing the client the real role all along.
    const g = await guest('LatePromotion');
    await signIn(page, g);

    await page.goto('/account');
    await expect(page.getByRole('link', { name: /host-dashboard/i })).toHaveCount(0);

    // Promote mid-session. The token in localStorage is deliberately NOT refreshed.
    await api.setRole(adminToken, g.userId, 'host');
    const claim = JSON.parse(Buffer.from(g.jwt.split('.')[1], 'base64').toString());
    expect(
      claim.role,
      'the token must still carry the stale claim for this to prove anything'
    ).toBe('guest');

    await page.reload();
    await expect(
      page.getByRole('link', { name: /host-dashboard/i }),
      'the live role from /me/context must win over the frozen JWT claim'
    ).toBeVisible({ timeout: 10_000 });
  });

  test('a host can remove the comment of a guest they have already banned', async ({
    page,
    api,
    host,
    guest,
    signIn,
  }) => {
    // The deadlock this closes. Ban first, exactly as a host would react to abuse: from then
    // on the author gets 403 on their own delete, so if the host has no removal affordance
    // the comment is stuck on screen forever.
    // The photo belongs to an innocent third party — a ban hides the banned user's OWN
    // uploads, so if the comment sat on their own photo the whole card would vanish and
    // there would be nothing left to moderate.
    const victim = await guest('PhotoOwner');
    const uploadId = await seedUpload(victim.jwt);
    const author = await guest('CommentOffender');
    const commentId = await seedComment(author.jwt, uploadId, 'unangebrachter Kommentar');
    await api.banUser(host.jwt, author.userId);

    // Confirm the deadlock really exists — the author cannot retract it themselves.
    const selfDelete = await fetch(`${BASE}/api/v1/comment/${commentId}`, {
      method: 'DELETE',
      headers: { Authorization: `Bearer ${author.jwt}` },
    });
    expect(selfDelete.status, 'a banned author is blocked from their own delete').toBe(403);

    await signIn(page, host);
    await page.goto('/feed');

    const card = page.locator('article').filter({ hasText: victim.displayName }).first();
    await expect(card).toBeVisible({ timeout: 15_000 });
    await card.getByRole('button', { name: 'Bild vergrößern' }).click();

    const comment = page.getByText('unangebrachter Kommentar');
    await expect(comment).toBeVisible({ timeout: 10_000 });
    await page.getByRole('button', { name: 'Kommentar entfernen' }).first().click();
    await expect(comment).toHaveCount(0, { timeout: 10_000 });
  });
});
