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)('categories flows (real PostgreSQL)', () => {
  const url = hasDb ? getTestDbUrl() : '';
  let pool: pg.Pool;
  let app: Awaited<ReturnType<typeof buildApp>>;
  let adminCookie = '';

  beforeAll(async () => {
    await recreateDatabase(url);
    await runMigrations(url, 'up');
    pool = createPool(url);
    app = await buildApp({ logger: silentLogger(), pool, cookieSecure: true });

    const user = { email: 'category-admin@example.com', password: 'correct horse battery staple' };
    const registered = await app.inject({
      method: 'POST',
      url: '/auth/register',
      headers: { 'content-type': 'application/json' },
      payload: user,
    });
    expect(registered.statusCode).toBe(201);
    const id = (registered.json() as { id: string }).id;
    await pool.query('UPDATE identity_users SET role = $1 WHERE id = $2', ['admin', id]);

    const login = await app.inject({
      method: 'POST',
      url: '/auth/login',
      headers: { 'content-type': 'application/json' },
      payload: user,
    });
    expect(login.statusCode).toBe(200);
    adminCookie = cookieValue(login.headers['set-cookie']);
  });

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

  it('returns HTTP 409 for duplicate slug (AC1)', async () => {
    const payload = {
      name: 'Aceites',
      slug: 'aceites',
      seoTitle: 'Aceites ecológicos',
      seoDescription: 'Aceites para alimentación saludable',
    };

    const first = await app.inject({
      method: 'POST',
      url: '/categories',
      headers: { 'content-type': 'application/json' },
      cookies: { [SESSION_COOKIE_NAME]: adminCookie },
      payload,
    });
    expect(first.statusCode).toBe(201);

    const duplicate = await app.inject({
      method: 'POST',
      url: '/categories',
      headers: { 'content-type': 'application/json' },
      cookies: { [SESSION_COOKIE_NAME]: adminCookie },
      payload,
    });
    expect(duplicate.statusCode).toBe(409);
    expect(duplicate.json().error.code).toBe('CATEGORY_SLUG_EXISTS');
  });

  it('supports parent/child tree and blocks cycles (AC2)', async () => {
    const root = await app.inject({
      method: 'POST',
      url: '/categories',
      headers: { 'content-type': 'application/json' },
      cookies: { [SESSION_COOKIE_NAME]: adminCookie },
      payload: { name: 'Alimentación', slug: 'alimentacion', isParent: true },
    });
    expect(root.statusCode).toBe(201);
    const rootBody = root.json() as { id: string };

    const child = await app.inject({
      method: 'POST',
      url: '/categories',
      headers: { 'content-type': 'application/json' },
      cookies: { [SESSION_COOKIE_NAME]: adminCookie },
      payload: { parentId: rootBody.id, name: 'Cereales', slug: 'cereales' },
    });
    expect(child.statusCode).toBe(201);
    const childBody = child.json() as { id: string };

    const tree = await app.inject({ method: 'GET', url: '/categories/tree' });
    expect(tree.statusCode).toBe(200);
    const treeBody = tree.json() as {
      items: Array<{ slug: string; children: Array<{ slug: string }> }>;
    };
    const rootNode = treeBody.items.find((item) => item.slug === 'alimentacion');
    expect(rootNode?.children.some((item) => item.slug === 'cereales')).toBe(true);

    const cycle = await app.inject({
      method: 'PATCH',
      url: `/categories/${rootBody.id}`,
      headers: { 'content-type': 'application/json' },
      cookies: { [SESSION_COOKIE_NAME]: adminCookie },
      payload: { parentId: childBody.id },
    });
    expect(cycle.statusCode).toBe(422);
    expect(cycle.json().error.code).toBe('CATEGORY_TREE_CYCLE');
  });

  it('serves public category URL by slug, never internal id (AC3)', async () => {
    const response = await app.inject({ method: 'GET', url: '/categoria/alimentacion' });

    expect(response.statusCode).toBe(200);
    expect(response.json()).toMatchObject({ slug: 'alimentacion', url: '/categoria/alimentacion' });
  });
});
