import { writeFile } from 'node:fs/promises';
import { resolve } from 'node:path';
import { afterEach, describe, expect, it } from 'vitest';
import { FinancialImportService, FinancialWorkbookParser, sha256 } from '../../src/services/excel-import-service.js';
import { InvoiceService } from '../../src/services/invoice-service.js';
import { createDocumentedFinancialSample } from '../fixtures/financial-sample.js';
import { createTestContext } from '../helpers.js';

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

describe('ورود مالی، نسخه‌بندی و مغایرت', () => {
  it('fixture مستند را با ۱۸۰ ردیف، ۱۱۰ فاکتور و ۱۹ کالا می‌خواند و footer را رد می‌کند', async () => {
    const buffer = await createDocumentedFinancialSample();
    FinancialWorkbookParser.validateEnvelope('خروجی.xlsm', 'application/vnd.ms-excel.sheet.macroenabled.12', buffer, 20 * 1024 * 1024);
    const parsed = await FinancialWorkbookParser.parse(buffer);
    expect(parsed.rows).toHaveLength(180);
    expect(parsed.groups).toHaveLength(110);
    expect(new Set(parsed.rows.map((row) => row.productCode)).size).toBe(19);
    expect(parsed.errors).toHaveLength(1);
    expect(parsed.errors[0]!.rowNumber).toBe(182);
    expect(parsed.errors[0]!.reasons.join(' ')).toMatch(/تاریخ|عنوان مشتری|کالا/);
  });

  it('import تکراری را idempotent و تغییر مالی را نسخه جدید نگه می‌دارد', async () => {
    const context = await createTestContext(); contexts.push(context);
    const service = new FinancialImportService(context.db);
    const actorId = context.users.admin!.id;
    const firstBuffer = await createDocumentedFinancialSample();
    const firstPath = resolve('storage/private/imports/test-first.xlsx'); await writeFile(firstPath, firstBuffer);
    const first = await service.register({ filename: 'خروجی.xlsx', storedPath: firstPath, mimeType: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet', fileSize: firstBuffer.length, checksum: sha256(firstBuffer), actorId });
    expect(first.duplicate).toBe(false);
    const firstOutcome = await service.confirm(first.id, await FinancialWorkbookParser.parse(firstBuffer), { actorId });
    expect(firstOutcome).toMatchObject({ validRows: 180, rejectedRows: 1, uniqueInvoices: 110, uniqueProducts: 19, newInvoices: 110 });
    const invoices = await context.db.query<{ count: string }>('SELECT COUNT(*) AS count FROM invoices');
    expect(Number(invoices.rows[0]!.count)).toBe(110);

    const duplicate = await service.register({ filename: 'همان.xlsx', storedPath: firstPath, mimeType: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet', fileSize: firstBuffer.length, checksum: sha256(firstBuffer), actorId });
    expect(duplicate).toEqual({ id: first.id, duplicate: true });
    const repeatedOutcome = await service.confirm(first.id, await FinancialWorkbookParser.parse(firstBuffer), { actorId });
    expect(repeatedOutcome).toEqual(firstOutcome);

    const changedBuffer = await createDocumentedFinancialSample(99);
    const changedPath = resolve('storage/private/imports/test-changed.xlsx'); await writeFile(changedPath, changedBuffer);
    const changed = await service.register({ filename: 'خروجی-جدید.xlsx', storedPath: changedPath, mimeType: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet', fileSize: changedBuffer.length, checksum: sha256(changedBuffer), actorId });
    const changedOutcome = await service.confirm(changed.id, await FinancialWorkbookParser.parse(changedBuffer), { actorId });
    expect(changedOutcome.updatedInvoices).toBe(1);
    expect(changedOutcome.unchangedInvoices).toBe(109);
    const versionCount = await context.db.query<{ count: string }>(
      `SELECT COUNT(*) AS count FROM invoice_versions v JOIN invoices i ON i.id = v.invoice_id WHERE i.normalized_number = '000001' AND v.source = 'financial'`
    );
    expect(Number(versionCount.rows[0]!.count)).toBe(2);
    expect((await context.db.query('SELECT id FROM financial_changes')).rowCount).toBe(1);
  });

  it('نسخه دستی و مالی را کنار هم مغایرت می‌دهد و انتخاب مدیر را علامت می‌زند', async () => {
    const context = await createTestContext(); contexts.push(context);
    const invoiceService = new InvoiceService(context.db);
    await invoiceService.createManual(context.users.supply!.id, {
      invoiceNumber: '000001', requesterId: context.requesterId, customerName: 'مشتری دستی', customerCode: 'M-1',
      items: [{ productCode: '0001', productName: 'نام مدیر', unit: 'عدد', quantity: 2 }]
    }, { actorId: context.users.supply!.id });
    const buffer = await createDocumentedFinancialSample();
    const path = resolve('storage/private/imports/manual-conflict.xlsx'); await writeFile(path, buffer);
    const importer = new FinancialImportService(context.db);
    const registered = await importer.register({ filename: 'مالی.xlsx', storedPath: path, mimeType: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet', fileSize: buffer.length, checksum: sha256(buffer), actorId: context.users.supply!.id });
    await importer.confirm(registered.id, await FinancialWorkbookParser.parse(buffer), { actorId: context.users.supply!.id });
    const discrepancy = await context.db.query<{ id: string }>(
      `SELECT d.id FROM discrepancies d JOIN invoices i ON i.id = d.invoice_id WHERE i.normalized_number = '000001' AND d.kind = 'manual_financial' AND d.status = 'open'`
    );
    expect(discrepancy.rowCount).toBe(1);
    await invoiceService.resolveDiscrepancy(context.users.supply!.id, discrepancy.rows[0]!.id,
      { customerName: 'manager' }, { '0001': 'manager' }, 'تأیید اطلاعات نسخه مدیر', { actorId: context.users.supply!.id });
    const resolved = await context.db.query<{ manager_edited: boolean; has_discrepancy: boolean; source: string }>(
      `SELECT i.manager_edited, i.has_discrepancy, v.source FROM invoices i JOIN invoice_versions v ON v.id = i.operational_version_id WHERE i.normalized_number = '000001'`
    );
    expect(resolved.rows[0]).toMatchObject({ manager_edited: true, has_discrepancy: false, source: 'resolved' });
  });
});
