// Compose round-trip — POST a status, fetch it back, delete it.
//
// Per-protocol shape (the reason the two protocols don't share assertions):
//   - Pixelfed is image-first. A text-only top-level status is rejected with
//     `403 {"error":"Empty statuses are not allowed"}` — the server requires
//     `media_ids`, an `in_reply_to_id`, or a quote. So the Pixelfed round-trip
//     uploads a tiny image first and posts it with `media_ids`, mirroring the
//     app's primary compose action.
//   - Mastodon allows text-only statuses, so its round-trip posts plain text.
//
// `unlisted` visibility keeps the test post off public timelines (it still
// federates, but federation isn't tested here); the `afterAll` cleanup hook
// deletes the post even if an assertion failed mid-test, so a flaky run does
// not leave dangling content on the bot account.
//
// We use `api.post` (NOT `postIdempotent`) — idempotency-key dedup is a
// production double-submit guard; the test surface is the wire round-trip,
// and Pixelfed's server-side idempotency state can leak across processes in
// ways that cause spurious failures.

import { ContextForActiveAccount } from 'src/lib/api-context'
import { describeWithRigType } from '../_helpers/loadTestRig'
import { RigCleanupTracker } from './_setup/cleanup'
import { __resetIntegrationMocks } from './_setup/jestMocks'
import { uploadTestImage } from './_setup/media'
import { seedAndActivate } from './_setup/seedAccount'

type StatusResponse = {
  id: string
  content: string
  media_attachments?: unknown[]
}

function makeRoundTrip(type: 'pixelfed' | 'mastodon') {
  describeWithRigType(type, `compose round-trip (${type})`, (entry) => {
    const cleanup = new RigCleanupTracker()

    beforeAll(() => {
      __resetIntegrationMocks()
      seedAndActivate(entry)
    })

    afterAll(async () => {
      await cleanup.drain()
    })

    it('publishes a status, reads it back, and registers cleanup', async () => {
      const api = ContextForActiveAccount()
      const body = `halflight integration test ${Date.now()}-${Math.random()
        .toString(36)
        .slice(2, 8)} — safe to delete`

      const params: Record<string, unknown> = { status: body, visibility: 'unlisted' }
      // Pixelfed forbids text-only top-level posts — attach a real image.
      // Mastodon takes text-only.
      if (type === 'pixelfed') {
        params.media_ids = [await uploadTestImage(entry)]
      }

      const created = (await api.post('/api/v1/statuses', params)) as StatusResponse
      expect(created.id).toBeTruthy()
      cleanup.trackStatus(api, created.id)

      // Read it back. Pixelfed's `_pe=1` adds extra fields; both protocols
      // return the same `content` and `id` we asserted on the way out.
      const fetched = (await api.get(`/api/v1/statuses/${created.id}`)) as StatusResponse
      expect(fetched.id).toBe(created.id)
      expect(typeof fetched.content).toBe('string')
      // The server may rewrap user text in `<p>...</p>` (Mastodon does;
      // Pixelfed does sometimes). Match on the stable prefix, not the literal.
      expect(fetched.content).toContain('halflight integration test')
      // The image rode along: the attachment relation is created synchronously
      // at status-create time (only derivative processing is async).
      if (type === 'pixelfed') {
        expect(Array.isArray(fetched.media_attachments)).toBe(true)
        expect((fetched.media_attachments as unknown[]).length).toBeGreaterThan(0)
      }
    })
  })
}

makeRoundTrip('pixelfed')
makeRoundTrip('mastodon')
