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

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

    const user = { email: 'catalog-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']);

    const category = await app.inject({
      method: 'POST',
      url: '/categories',
      headers: { 'content-type': 'application/json' },
      cookies: { [SESSION_COOKIE_NAME]: adminCookie },
      payload: { name: 'Aceites', slug: 'catalogo-aceites' },
    });
    expect(category.statusCode).toBe(201);
    categoryId = (category.json() as { id: string }).id;

    const brand = await app.inject({
      method: 'POST',
      url: '/brands',
      headers: { 'content-type': 'application/json' },
      cookies: { [SESSION_COOKIE_NAME]: adminCookie },
      payload: { name: 'Bio Brand', slug: 'bio-brand' },
    });
    expect(brand.statusCode).toBe(201);
    brandId = (brand.json() as { id: string }).id;
  });

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

  it('returns HTTP 409 for duplicate product slug (AC2)', async () => {
    const payload = {
      name: 'Aceite de oliva',
      slug: 'aceite-oliva',
      state: 'active',
      categoryIds: [categoryId],
      brandId,
    };

    const first = await app.inject({
      method: 'POST',
      url: '/products',
      headers: { 'content-type': 'application/json' },
      cookies: { [SESSION_COOKIE_NAME]: adminCookie },
      payload,
    });
    expect(first.statusCode).toBe(201);
    productId = (first.json() as { id: string }).id;
    expect(first.json().categoryIds).toEqual([categoryId]);
    expect(first.json().brandId).toBe(brandId);

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

  it('public listing and slug reads expose only active products (AC3, AC4)', async () => {
    const draft = await app.inject({
      method: 'POST',
      url: '/products',
      headers: { 'content-type': 'application/json' },
      cookies: { [SESSION_COOKIE_NAME]: adminCookie },
      payload: { name: 'Borrador', slug: 'producto-borrador', state: 'draft' },
    });
    expect(draft.statusCode).toBe(201);

    const search = await app.inject({ method: 'GET', url: '/products/search?q=producto' });
    expect(search.statusCode).toBe(200);
    const body = search.json() as { items: Array<{ slug: string }> };
    expect(body.items.some((item) => item.slug === 'producto-borrador')).toBe(false);

    const draftBySlug = await app.inject({ method: 'GET', url: '/productos/producto-borrador' });
    expect(draftBySlug.statusCode).toBe(404);

    const activeBySlug = await app.inject({ method: 'GET', url: '/productos/aceite-oliva' });
    expect(activeBySlug.statusCode).toBe(200);
    expect(activeBySlug.json()).toMatchObject({
      slug: 'aceite-oliva',
      url: '/productos/aceite-oliva',
    });
  });

  it('filters public products by brand slug (F-009 AC2)', async () => {
    const otherBrand = await app.inject({
      method: 'POST',
      url: '/brands',
      headers: { 'content-type': 'application/json' },
      cookies: { [SESSION_COOKIE_NAME]: adminCookie },
      payload: { name: 'Other Brand', slug: 'other-brand' },
    });
    expect(otherBrand.statusCode).toBe(201);
    const otherBrandId = (otherBrand.json() as { id: string }).id;

    const otherProduct = await app.inject({
      method: 'POST',
      url: '/products',
      headers: { 'content-type': 'application/json' },
      cookies: { [SESSION_COOKIE_NAME]: adminCookie },
      payload: {
        name: 'Producto otra marca',
        slug: 'producto-otra-marca',
        state: 'active',
        brandId: otherBrandId,
      },
    });
    expect(otherProduct.statusCode).toBe(201);

    const filtered = await app.inject({
      method: 'GET',
      url: '/products/search?brandSlug=bio-brand',
    });
    expect(filtered.statusCode).toBe(200);
    const body = filtered.json() as { items: Array<{ slug: string }> };
    expect(body.items.some((item) => item.slug === 'aceite-oliva')).toBe(true);
    expect(body.items.some((item) => item.slug === 'producto-otra-marca')).toBe(false);
  });

  it('returns HTTP 409 for duplicate variant SKU or EAN (F-010 AC1)', async () => {
    const first = await app.inject({
      method: 'POST',
      url: `/products/${productId}/variants`,
      headers: { 'content-type': 'application/json' },
      cookies: { [SESSION_COOKIE_NAME]: adminCookie },
      payload: { sku: 'SKU-ACEITE-1', ean: '8412345678901', attributes: { size: '500ml' } },
    });
    expect(first.statusCode).toBe(201);

    const duplicateSku = await app.inject({
      method: 'POST',
      url: `/products/${productId}/variants`,
      headers: { 'content-type': 'application/json' },
      cookies: { [SESSION_COOKIE_NAME]: adminCookie },
      payload: { sku: 'SKU-ACEITE-1', ean: '8412345678902' },
    });
    expect(duplicateSku.statusCode).toBe(409);
    expect(duplicateSku.json().error.code).toBe('PRODUCT_VARIANT_CODE_EXISTS');

    const duplicateEan = await app.inject({
      method: 'POST',
      url: `/products/${productId}/variants`,
      headers: { 'content-type': 'application/json' },
      cookies: { [SESSION_COOKIE_NAME]: adminCookie },
      payload: { sku: 'SKU-ACEITE-2', ean: '8412345678901' },
    });
    expect(duplicateEan.statusCode).toBe(409);
  });

  it('seeds a default price row immediately after variant creation (F-138 AC1, AC2, AC4)', async () => {
    const productRes = await app.inject({
      method: 'POST',
      url: '/products',
      headers: { 'content-type': 'application/json' },
      cookies: { [SESSION_COOKIE_NAME]: adminCookie },
      payload: {
        name: 'Producto Semilla F-138',
        slug: 'semilla-f138-ac1',
        categoryIds: [categoryId],
      },
    });
    expect(productRes.statusCode).toBe(201);
    const productId = (productRes.json() as { id: string }).id;

    const variantRes = await app.inject({
      method: 'POST',
      url: `/products/${productId}/variants`,
      headers: { 'content-type': 'application/json' },
      cookies: { [SESSION_COOKIE_NAME]: adminCookie },
      payload: { sku: 'SKU-SEMILLA-F138', ean: '8412345678902', attributes: { size: '500ml' } },
    });
    expect(variantRes.statusCode).toBe(201);
    const variantId = (variantRes.json() as { id: string }).id;

    // F-138 AC1/AC2: a default price row exists immediately after variant creation
    // (no 404 window on GET /pricing/variants/:id) and re-seeding is a no-op.
    const priceRow = await pool.query(
      'SELECT net_unit_amount_cents, vat_rate, currency FROM pricing_variant_prices WHERE variant_id = $1',
      [variantId],
    );
    expect(priceRow.rows[0]).toBeDefined();
    expect(priceRow.rows[0]!.net_unit_amount_cents).toBe(0);
    expect(priceRow.rows[0]!.vat_rate).toBe('general');
    expect(priceRow.rows[0]!.currency).toBe('EUR');
  });

  it('stores nutrition provenance and protects manual nutrition from external overwrite (F-010 AC2, AC3)', async () => {
    const manual = await app.inject({
      method: 'PATCH',
      url: `/products/${productId}/rich-data`,
      headers: { 'content-type': 'application/json' },
      cookies: { [SESSION_COOKIE_NAME]: adminCookie },
      payload: {
        ingredients: 'Aceite de oliva virgen extra',
        allergens: [],
        nutrition: { calories: 884, fat: 100 },
        nutritionSource: 'manual',
        isOrganic: true,
        organicCertification: 'EU Organic',
      },
    });
    expect(manual.statusCode).toBe(200);
    expect(manual.json()).toMatchObject({
      nutrition: { calories: 884, fat: 100 },
      nutritionSource: 'manual',
      isOrganic: true,
    });

    const external = await app.inject({
      method: 'PATCH',
      url: `/products/${productId}/rich-data`,
      headers: { 'content-type': 'application/json' },
      cookies: { [SESSION_COOKIE_NAME]: adminCookie },
      payload: {
        nutrition: { calories: 1 },
        nutritionSource: 'openfoodfacts',
        ingredients: 'External ingredients update allowed',
      },
    });
    expect(external.statusCode).toBe(200);
    expect(external.json()).toMatchObject({
      nutrition: { calories: 884, fat: 100 },
      nutritionSource: 'manual',
      ingredients: 'External ingredients update allowed',
    });
  });

  it('rejects unknown product category assignment', async () => {
    const response = await app.inject({
      method: 'POST',
      url: '/products',
      headers: { 'content-type': 'application/json' },
      cookies: { [SESSION_COOKIE_NAME]: adminCookie },
      payload: {
        name: 'Producto sin categoría real',
        slug: 'producto-categoria-invalida',
        categoryIds: ['00000000-0000-4000-8000-000000000000'],
      },
    });

    expect(response.statusCode).toBe(422);
    expect(response.json().error.code).toBe('PRODUCT_CATEGORY_NOT_FOUND');
  });
});
