/**
 * The contract regression test (PLANNING.md §12): hit the running app and parse
 * the gallery response through a verbatim copy of the frontend's own zod schema.
 * If a field name drifts, this fails in CI rather than silently in production.
 *
 * Needs a migrated + seeded database — skipped when one isn't reachable so a bare
 * `npm test` on a fresh checkout still passes the unit suite. CI runs a mysql:8
 * service and seeds it, so these DO run there.
 */
import request from 'supertest';
import { afterAll, describe, expect, it } from 'vitest';
import { app } from '../src/app';
import { destroyDb, pingDb } from '../src/lib/db';
import { enquiryResponseSchema, spaceGallerySchema } from '../src/types/contract';

const hasDb = (await pingDb().catch(() => ({ ok: false }))).ok;

afterAll(async () => {
  await destroyDb();
});

describe.skipIf(!hasDb)('public API (integration)', () => {
  it('GET /health -> 200 with a real DB ping', async () => {
    const res = await request(app).get('/health');
    expect(res.status).toBe(200);
    expect(res.body.db.status).toBe('ok');
  });

  it('GET /spaces/genesis/gallery matches the frontend contract', async () => {
    const res = await request(app).get('/spaces/genesis/gallery');
    expect(res.status).toBe(200);
    expect(res.body.slug).toBe('genesis');
    expect(res.headers['cache-control']).toContain('max-age=300');
    // The consumer's own parser — the point of the test.
    if (res.body.images.length > 0) {
      expect(() => spaceGallerySchema.parse(res.body)).not.toThrow();
    }
  });

  it('GET /spaces/nope/gallery -> 404', async () => {
    const res = await request(app).get('/spaces/nope/gallery');
    expect(res.status).toBe(404);
  });

  it('POST /enquiries — valid submission', async () => {
    const res = await request(app).post('/enquiries').send({
      name: 'Contract Test',
      phone: '+60123456789',
      email: 'contract-test@example.com',
      message: 'This is a valid enquiry body for the integration test.',
    });
    expect(res.status).toBe(200);
    expect(() => enquiryResponseSchema.parse(res.body)).not.toThrow();
    expect(res.body).toMatchObject({ ok: true });
  });

  it('POST /enquiries — validation failure returns 422 in the {ok:false} shape', async () => {
    const res = await request(app).post('/enquiries').send({ name: 'x' });
    expect(res.status).toBe(422);
    expect(res.body.ok).toBe(false);
    expect(res.body.fields).toBeTypeOf('object');
    expect(() => enquiryResponseSchema.parse(res.body)).not.toThrow();
  });

  it('POST /enquiries — honeypot looks like success', async () => {
    const res = await request(app).post('/enquiries').send({
      name: 'Spammy Bot',
      phone: '+60123456789',
      email: 'bot@example.com',
      message: 'buy my things right now please',
      website: 'http://spam.example',
    });
    expect(res.status).toBe(200);
    expect(res.body.ok).toBe(true);
  });

  it('GET /admin/enquiries without a token -> 401', async () => {
    const res = await request(app).get('/admin/enquiries');
    expect(res.status).toBe(401);
  });
});

describe.skipIf(hasDb)('public API (integration) — skipped, no database', () => {
  it('is skipped', () => {
    expect(true).toBe(true);
  });
});
