import request from 'supertest';
import * as ExcelJS from 'exceljs';
import { Harness, TenantClient } from './harness';

const MATMAS = `<?xml version="1.0"?><MATMAS05><IDOC BEGIN="1"><EDI_DC40 SEGMENT="1"><DOCNUM>0000000000123456</DOCNUM><IDOCTYP>MATMAS05</IDOCTYP><MESTYP>MATMAS</MESTYP><SNDPRN>SAPECC</SNDPRN></EDI_DC40>
<E1MARAM SEGMENT="1"><MATNR>000000000000500123</MATNR><MTART>FERT</MTART><MATKL>TV</MATKL><NTGEW>12.5</NTGEW><VOLUM>0.15</VOLUM><VOLEH>M3</VOLEH><EAN11>8806092051706</EAN11><XCHPF>X</XCHPF><MHDHB>0</MHDHB>
<E1MAKTM SEGMENT="1"><SPRAS_ISO>EN</SPRAS_ISO><MAKTX>Samsung 55 inch UHD TV</MAKTX></E1MAKTM>
<E1MARMM SEGMENT="1"><MEINH>CS</MEINH><UMREZ>2</UMREZ><UMREN>1</UMREN></E1MARMM></E1MARAM></IDOC></MATMAS05>`;
const ORDERS = `<?xml version="1.0"?><ORDERS05><IDOC BEGIN="1"><EDI_DC40 SEGMENT="1"><DOCNUM>0000000000123457</DOCNUM><IDOCTYP>ORDERS05</IDOCTYP><MESTYP>ORDERS</MESTYP></EDI_DC40>
<E1EDK01 SEGMENT="1"><BELNR>4500001234</BELNR></E1EDK01><E1EDK02 SEGMENT="1"><QUALF>001</QUALF><BELNR>4500001234</BELNR></E1EDK02><E1EDK03 SEGMENT="1"><IDDAT>002</IDDAT><DATUM>20261020</DATUM></E1EDK03>
<E1EDKA1 SEGMENT="1"><PARVW>LF</PARVW><PARTN>VEND01</PARTN></E1EDKA1>
<E1EDP01 SEGMENT="1"><POSEX>00010</POSEX><MENGE>40</MENGE><E1EDP19 SEGMENT="1"><QUALF>001</QUALF><IDTNR>000000000000500123</IDTNR></E1EDP19></E1EDP01></IDOC></ORDERS05>`;

describe('Integrations: public API v1, SAP IDoc adapter, webhooks, Excel imports', () => {
  const h = new Harness();
  let t: TenantClient;
  let storerId: string;
  let key: string;

  beforeAll(async () => {
    await h.start();
    t = await h.tenant('int');
    storerId = (await t.standardWorld()).storer.id;
    key = (await t.post('/api-keys', { name: 'Acme ERP', storerId, scopes: ['items:write', 'items:read', 'asn:write', 'asn:read', 'orders:write', 'orders:read', 'inventory:read', 'serials:read'] }).expect(201)).body.key;
  });
  afterAll(() => h.stop());

  it('v1 items upsert, ASN with Idempotency-Key, order creation and inventory read (codes, not ids)', async () => {
    const k = t.apiKey(key);
    const up = (await k.put('/v1/items', { items: [{ code: 'GADGET-9', description: 'Gadget 9', barcodes: [{ barcode: '6291000000099' }], packs: [{ level: 'CASE', uomCode: 'CS', qtyInBase: 6 }] }] }).expect(200)).body;
    expect(up.created + up.updated).toBe(1);
    const a1 = (await k.post('/v1/asns', { warehouseCode: 'KW1', reference: 'PO-2001', lines: [{ itemCode: 'GADGET-9', qty: 10, packLevel: 'CASE', lot: 'G1', expiryDate: '2027-06-30' }] }, { 'idempotency-key': 'po-2001' }).expect(201)).body;
    const a2 = (await k.post('/v1/asns', { warehouseCode: 'KW1', reference: 'PO-2001', lines: [{ itemCode: 'GADGET-9', qty: 99 }] }, { 'idempotency-key': 'po-2001' }).expect(201)).body;
    expect(a2.asnNumber).toBe(a1.asnNumber); // replay returns the original
    const got = (await k.get('/v1/asns/PO-2001').expect(200)).body;
    expect(got.lines).toHaveLength(1);
    expect(Number(got.lines[0].expected)).toBe(60);
    await k.post('/v1/orders', { warehouseCode: 'KW1', reference: 'SO-9001', shipTo: { name: 'Store Z' }, lines: [{ itemCode: 'WIDGET-1', qty: 2 }] }).expect(201);
    const inv = (await k.get('/v1/inventory', { groupBy: 'ITEM' }).expect(200)).body;
    expect(Array.isArray(inv)).toBe(true);
    const bad = await k.post('/v1/asns', { warehouseCode: 'KW1', reference: 'PO-X', lines: [{ itemCode: 'UNKNOWN', qty: 1 }] }).expect(404);
    expect(bad.body.code).toBe('NOT_FOUND');
  });

  it('SAP IDoc: MATMAS creates items, ORDERS creates an ASN, message log tracks both', async () => {
    const conn = (await t.post('/integrations/connectors', { type: 'SAP_IDOC', name: 'SAP ECC', storerId, config: { warehouseCode: 'KW1', storageLocation: 'KW01', sapPassword: 's3cret' } }).expect(201)).body;
    expect(JSON.stringify(conn.config)).not.toContain('s3cret'); // secrets are masked on read
    await request(h.server).post(`/api/v1/erp/${conn.id}/inbound`).set('x-api-key', key).set('content-type', 'application/xml').send(MATMAS).expect(202);
    await request(h.server).post(`/api/v1/erp/${conn.id}/inbound`).set('x-api-key', key).set('content-type', 'application/xml').send(ORDERS).expect(202);
    const asn = (await t.apiKey(key).get('/v1/asns/4500001234').expect(200)).body;
    expect(asn.lines[0].itemCode).toBe('500123');
    expect(Number(asn.lines[0].expected)).toBe(40);
    const msgs = (await t.get('/integrations/messages').expect(200)).body.items;
    expect(msgs.filter((m: { messageType: string }) => ['MATMAS', 'ORDERS'].includes(m.messageType)).every((m: { status: string }) => m.status === 'PROCESSED')).toBe(true);
  });

  it('webhooks: subscription gets a secret once, deliveries are queued per event with HMAC signature', async () => {
    const hook = (await t.post('/integrations/webhooks', { name: 'ERP hook', url: 'http://127.0.0.1:9/hook', events: ['asn.created', 'order.created'] }).expect(201)).body;
    expect(hook.secret.length).toBeGreaterThanOrEqual(32);
    const again = (await t.get('/integrations/webhooks').expect(200)).body;
    const row = (again.items ?? again).find((w: { id: string }) => w.id === hook.id);
    expect(row.secret).toBeUndefined();
    await t.post('/inbound/asns', { warehouseId: t.warehouseId, storerId, externalRef: 'PO-HOOK', lines: [{ skuCode: 'WIDGET-1', expectedQty: '1' }] }).expect(201);
    await h.drainOutbox();
    const deliveries = (await t.get(`/integrations/webhooks/${hook.id}/deliveries`).expect(200)).body.items;
    expect(deliveries.some((d: { eventType: string }) => d.eventType === 'asn.created')).toBe(true);
  });

  it('Excel import: template download, dry run reports row errors, real run creates SKUs', async () => {
    const tpl = await t.get('/integrations/imports/template/SKU').expect(200);
    expect(tpl.headers['content-type']).toContain('spreadsheet');
    const wb = new ExcelJS.Workbook();
    const ws = wb.addWorksheet('SKUs');
    ws.addRow(['SKU Code', 'Description', 'Category', 'Storage Condition', 'Rotation', 'Lot Controlled (Y/N)', 'Expiry Controlled (Y/N)', 'Case Qty', 'Barcode Each']);
    ws.addRow(['IMP-1', 'Imported item 1', 'Imports', 'AMBIENT', 'FIFO', 'N', 'N', '24', '6291000000101']);
    ws.addRow(['IMP-2', 'Imported item 2', 'Imports', 'CHILLED', 'FEFO', 'Y', 'Y', '12', '6291000000102']);
    ws.addRow(['', 'Missing code', 'Imports', 'AMBIENT', 'FIFO', 'N', 'N', '1', '']);
    const buf = Buffer.from(await wb.xlsx.writeBuffer());
    const dry = (await request(h.server).post('/api/integrations/imports').set('authorization', `Bearer ${t.token}`).field('type', 'SKU').field('storerId', storerId).field('dryRun', 'true').attach('file', buf, 'skus.xlsx').expect(201)).body;
    expect(dry.successRows).toBe(2);
    expect(dry.errors.length).toBeGreaterThanOrEqual(1);
    expect(dry.errors.some((e: { row: number }) => e.row === 4)).toBe(true);
    const before = (await t.get('/skus', { storerId }).expect(200)).body.total;
    const real = (await request(h.server).post('/api/integrations/imports').set('authorization', `Bearer ${t.token}`).field('type', 'SKU').field('storerId', storerId).attach('file', buf, 'skus.xlsx').expect(201)).body;
    expect(real.successRows).toBe(2);
    const after = (await t.get('/skus', { storerId }).expect(200)).body.total;
    expect(after).toBe(before + 2);
  });
});
