import { afterEach, describe, expect, it } from 'vitest';
import { DeliveryService } from '../../src/services/delivery-service.js';
import { InvoiceService } from '../../src/services/invoice-service.js';
import { createTestContext } from '../helpers.js';

const contexts: Awaited<ReturnType<typeof createTestContext>>[] = [];
afterEach(async () => { while (contexts.length) await contexts.pop()!.db.close(); });

async function manual(context: Awaited<ReturnType<typeof createTestContext>>, number: string, quantities = [5, 3]) {
  return new InvoiceService(context.db).createManual(context.users.supply!.id, {
    invoiceNumber: number, requesterId: context.requesterId, customerName: 'مشتری آزمایشی',
    items: quantities.map((quantity, index) => ({ productCode: `P-${index + 1}`, productName: `کالا ${index + 1}`, unit: 'عدد', quantity }))
  }, { actorId: context.users.supply!.id });
}

describe('گردش کامل تحویل', () => {
  it('تحویل کامل اتمیک، جلوگیری از بیش‌تحویلی و idempotency را اعمال می‌کند', async () => {
    const context = await createTestContext(); contexts.push(context);
    const invoiceId = await manual(context, 'M-100');
    const published: any[] = [];
    const service = new DeliveryService(context.db, async (channel, payload) => { published.push({ channel, payload }); });
    const request = await service.approve({ id: context.users.supply!.id }, {
      invoiceId, requesterId: context.requesterId, warehouseId: context.warehouseId, deliveryType: 'complete',
      items: [
        { productCode: 'P-1', productName: 'کالا 1', unit: 'عدد', quantity: 5 },
        { productCode: 'P-2', productName: 'کالا 2', unit: 'عدد', quantity: 3 }
      ], idempotencyKey: 'approve-complete'
    }, { actorId: context.users.supply!.id });
    expect(request.status).toBe('pending');
    expect(published[0].channel).toBe(`warehouse:${context.warehouseId}`);
    await expect(service.approve({ id: context.users.supply!.id }, {
      invoiceId, requesterId: context.requesterId, warehouseId: context.warehouseId, deliveryType: 'partial',
      items: [{ productCode: 'P-1', productName: 'کالا 1', unit: 'عدد', quantity: 1 }], idempotencyKey: 'over'
    }, {})).rejects.toMatchObject({ code: 'OVER_DELIVERY' });
    const actor = { id: context.users.warehouse!.id, warehouseId: context.warehouseId };
    const [first, second] = await Promise.all([
      service.warehouseAction(actor, request.id, 'deliver', 'deliver-once', undefined, {}),
      service.warehouseAction(actor, request.id, 'deliver', 'deliver-once', undefined, {})
    ]);
    expect(first.state).toBe('delivered'); expect(second.state).toBe('delivered');
    const delivered = await context.db.query<{ count: string }>(
      `SELECT COUNT(*) AS count FROM delivery_events WHERE delivery_request_id = $1 AND event_type = 'delivered'`, [request.id]
    );
    expect(Number(delivered.rows[0]!.count)).toBe(1);
  });

  it('تحویل جزئی چندمرحله‌ای، لغو با دلیل، برگشت و تحویل مجدد را حفظ می‌کند', async () => {
    const context = await createTestContext(); contexts.push(context);
    const invoiceId = await manual(context, 'M-200', [5]);
    const service = new DeliveryService(context.db);
    const first = await service.approve({ id: context.users.supply!.id }, {
      invoiceId, requesterId: context.requesterId, warehouseId: context.warehouseId, deliveryType: 'partial',
      items: [{ productCode: 'P-1', productName: 'کالا 1', unit: 'عدد', quantity: 2 }], idempotencyKey: 'partial-1'
    }, {});
    const warehouse = { id: context.users.warehouse!.id, warehouseId: context.warehouseId };
    await service.warehouseAction(warehouse, first.id, 'deliver', 'd1', undefined, {});
    await expect(service.warehouseAction(warehouse, first.id, 'cancel', 'c-bad', '', {})).rejects.toMatchObject({ code: 'REASON_REQUIRED' });
    await service.warehouseAction(warehouse, first.id, 'return', 'r1', 'کالا به انبار برگشت', {});
    await service.warehouseAction(warehouse, first.id, 'deliver', 'd2', undefined, {});
    const second = await service.approve({ id: context.users.supply!.id }, {
      invoiceId, requesterId: context.requesterId, warehouseId: context.warehouseId, deliveryType: 'complete',
      items: [{ productCode: 'P-1', productName: 'کالا 1', unit: 'عدد', quantity: 3 }], idempotencyKey: 'round-2'
    }, {});
    await service.warehouseAction(warehouse, second.id, 'cancel', 'cancel-round', 'لغو با هماهنگی مدیر', {});
    const remaining = await service.remainingQuantities(context.db, invoiceId);
    expect(remaining.get('P-1')).toBe(3);
    const events = await context.db.query<{ event_type: string }>('SELECT event_type FROM delivery_events WHERE delivery_request_id = $1 ORDER BY sequence', [first.id]);
    expect(events.rows.map((event) => event.event_type)).toEqual(['approved', 'delivered', 'returned', 'redelivered']);
  });

  it('انباردار درخواست انبار نامرتبط را نمی‌بیند/تغییر نمی‌دهد و audit قابل ویرایش نیست', async () => {
    const context = await createTestContext(); contexts.push(context);
    const invoiceId = await manual(context, 'M-300', [1]);
    const service = new DeliveryService(context.db);
    const request = await service.approve({ id: context.users.supply!.id }, {
      invoiceId, requesterId: context.requesterId, warehouseId: context.warehouseId, deliveryType: 'complete',
      items: [{ productCode: 'P-1', productName: 'کالا 1', unit: 'عدد', quantity: 1 }], idempotencyKey: 'visibility'
    }, {});
    await expect(service.warehouseAction({ id: context.users.warehouse!.id, warehouseId: null }, request.id, 'deliver', 'unauthorized', undefined, {}))
      .rejects.toMatchObject({ code: 'FORBIDDEN' });
    const audit = await context.db.query<{ id: string }>('SELECT id FROM audit_events LIMIT 1');
    await expect(context.db.query(`UPDATE audit_events SET action = 'tampered' WHERE id = $1`, [audit.rows[0]!.id])).rejects.toThrow(/immutable_record/);
  });

  it('فاکتور دستی بدون قلم معنی تحویل کامل فاقد جزئیات را حفظ می‌کند', async () => {
    const context = await createTestContext(); contexts.push(context);
    const invoiceId = await new InvoiceService(context.db).createManual(context.users.supply!.id, {
      invoiceNumber: 'M-NO-LINES', requesterId: context.requesterId, items: []
    }, {});
    const request = await new DeliveryService(context.db).approve({ id: context.users.supply!.id }, {
      invoiceId, requesterId: context.requesterId, warehouseId: context.warehouseId, deliveryType: 'complete',
      wholeInvoiceNoLines: true, items: [], idempotencyKey: 'no-lines'
    }, {});
    const stored = await context.db.query<{ whole_invoice_no_lines: boolean }>('SELECT whole_invoice_no_lines FROM delivery_requests WHERE id = $1', [request.id]);
    expect(stored.rows[0]!.whole_invoice_no_lines).toBe(true);
  });
});

