import { randomUUID } from 'node:crypto';
import type { DestinationStream } from 'pino';
import type pg from 'pg';
import { afterAll, beforeAll, describe, expect, it } from 'vitest';
import { buildApp } from '../build-app.js';
import { createPool } from '../../infrastructure/db/pool.js';
import { createLogger } from '../../infrastructure/logging/logger.js';
import {
  getTestDbUrl,
  recreateDatabase,
  runMigrations,
} from '../../infrastructure/db/tests/db-test-support.js';
import { SESSION_COOKIE_NAME } from '../../modules/identity/index.js';

const hasDb = Boolean(process.env.TEST_DATABASE_URL);
function silentLogger() {
  const destination: DestinationStream = { write: () => undefined };
  return createLogger({ level: 'info', destination });
}
function cookieValue(setCookieHeader: string | string[] | undefined): string {
  const raw = Array.isArray(setCookieHeader) ? setCookieHeader[0] : setCookieHeader;
  expect(raw).toBeDefined();
  const pair = (raw as string).split(';')[0] as string;
  return pair.slice(pair.indexOf('=') + 1);
}

describe.skipIf(!hasDb)('orders flows (real PostgreSQL)', () => {
  const url = hasDb ? getTestDbUrl() : '';
  let pool: pg.Pool;
  let app: Awaited<ReturnType<typeof buildApp>>;
  let cookie = '';

  beforeAll(async () => {
    await recreateDatabase(url);
    await runMigrations(url, 'up');
    pool = createPool(url);
    app = await buildApp({ logger: silentLogger(), pool, cookieSecure: true });
    const user = { email: 'orders-user@example.com', password: 'correct horse battery staple' };
    await app.inject({
      method: 'POST',
      url: '/auth/register',
      headers: { 'content-type': 'application/json' },
      payload: user,
    });
    const login = await app.inject({
      method: 'POST',
      url: '/auth/login',
      headers: { 'content-type': 'application/json' },
      payload: user,
    });
    cookie = cookieValue(login.headers['set-cookie']);
  });

  afterAll(async () => {
    await app.close();
    await pool.end();
  });

  async function createOrder() {
    const productId = randomUUID();
    const variantId = randomUUID();
    return app.inject({
      method: 'POST',
      url: '/orders',
      headers: { 'content-type': 'application/json' },
      cookies: { [SESSION_COOKIE_NAME]: cookie },
      payload: {
        items: [
          {
            productId,
            variantId,
            sku: 'SKU-1',
            ean: null,
            name: 'Original Name',
            unitPriceCents: 1000,
            discountCents: 0,
            taxCents: 210,
            quantity: 1,
          },
        ],
        totals: { subtotalCents: 1000, discountCents: 0, taxCents: 210, totalCents: 1210 },
      },
    });
  }

  it('keeps snapshot values when snapshot data does not change (AC1)', async () => {
    const created = await createOrder();
    const id = (created.json() as { id: string }).id;
    expect(id.length).toBeGreaterThan(0);

    const fetched = await app.inject({
      method: 'GET',
      url: `/orders/${id}`,
      cookies: { [SESSION_COOKIE_NAME]: cookie },
    });
    expect(fetched.json()).toMatchObject({
      items: [{ name: 'Original Name', unitPriceCents: 1000, taxCents: 210 }],
    });
  });

  it('rejects SHIPPED -> PENDING transition (AC2)', async () => {
    const created = await createOrder();
    const id = (created.json() as { id: string }).id;
    for (const state of ['AWAITING_PAYMENT', 'PAID', 'PROCESSING', 'SHIPPED']) {
      const response = await app.inject({
        method: 'POST',
        url: `/orders/${id}/transitions`,
        headers: { 'content-type': 'application/json' },
        cookies: { [SESSION_COOKIE_NAME]: cookie },
        payload: { state },
      });
      expect(response.statusCode).toBe(200);
    }
    const illegal = await app.inject({
      method: 'POST',
      url: `/orders/${id}/transitions`,
      headers: { 'content-type': 'application/json' },
      cookies: { [SESSION_COOKIE_NAME]: cookie },
      payload: { state: 'PENDING' },
    });
    expect(illegal.statusCode).toBe(409);
    expect((illegal.json().error as { code: string }).code).toBe('ORDER_STATE_TRANSITION_INVALID');
  });

  it('stores an OrderCreated event log row by including state PENDING', async () => {
    const created = await createOrder();
    expect((created.json() as { state: string }).state).toBe('PENDING');
  });
});
