import { tap, expect } from '@push.rocks/tapbundle';
import { XInvoice } from '../ts/classes.xinvoice.js';
import { ValidationLevel, InvoiceFormat } from '../ts/interfaces/common.js';
import * as fs from 'fs/promises';
import * as path from 'path';

// Test loading and parsing real CII (Factur-X/ZUGFeRD) XML files
tap.test('XInvoice should load and parse real CII XML files', async () => {
  // Test with a simple CII file
  const xmlPath = path.join(process.cwd(), 'test/assets/corpus/XML-Rechnung/CII/EN16931_Einfach.cii.xml');
  const xmlContent = await fs.readFile(xmlPath, 'utf8');

  // Create XInvoice from XML
  const xinvoice = await XInvoice.fromXml(xmlContent);

  // Check that the XInvoice instance has the expected properties
  expect(xinvoice).toBeTruthy();
  expect(xinvoice.from).toBeTruthy();
  expect(xinvoice.to).toBeTruthy();
  expect(xinvoice.items).toBeArray();

  // Check that the format is detected correctly
  expect(xinvoice.getFormat()).toEqual(InvoiceFormat.FACTURX);

  // Check that the invoice can be exported back to XML
  const exportedXml = await xinvoice.exportXml('facturx');
  expect(exportedXml).toBeTruthy();
  expect(exportedXml).toInclude('CrossIndustryInvoice');

  // Save the exported XML for inspection
  const testDir = path.join(process.cwd(), 'test', 'output');
  await fs.mkdir(testDir, { recursive: true });
  await fs.writeFile(path.join(testDir, 'real-cii-exported.xml'), exportedXml);
});

// Test loading and parsing real UBL (XRechnung) XML files
tap.test('XInvoice should load and parse real UBL XML files', async () => {
  // Test with a simple UBL file
  const xmlPath = path.join(process.cwd(), 'test/assets/corpus/XML-Rechnung/UBL/EN16931_Einfach.ubl.xml');
  const xmlContent = await fs.readFile(xmlPath, 'utf8');

  // Create XInvoice from XML
  const xinvoice = await XInvoice.fromXml(xmlContent);

  // Check that the XInvoice instance has the expected properties
  expect(xinvoice).toBeTruthy();
  expect(xinvoice.from).toBeTruthy();
  expect(xinvoice.to).toBeTruthy();
  expect(xinvoice.items).toBeArray();

  // Check that the format is detected correctly
  expect(xinvoice.getFormat()).toEqual(InvoiceFormat.XRECHNUNG);

  // Check that the invoice can be exported back to XML
  const exportedXml = await xinvoice.exportXml('xrechnung');
  expect(exportedXml).toBeTruthy();
  expect(exportedXml).toInclude('Invoice');

  // Save the exported XML for inspection
  const testDir = path.join(process.cwd(), 'test', 'output');
  await fs.mkdir(testDir, { recursive: true });
  await fs.writeFile(path.join(testDir, 'real-ubl-exported.xml'), exportedXml);
});

// Test PDF creation and extraction with real XML files
tap.test('XInvoice should create and parse PDFs with embedded XML', async () => {
  // Find a real CII XML file to use
  const xmlPath = path.join(process.cwd(), 'test/assets/corpus/XML-Rechnung/CII/EN16931_Einfach.cii.xml');
  const xmlContent = await fs.readFile(xmlPath, 'utf8');

  // Create XInvoice from XML
  const xinvoice = await XInvoice.fromXml(xmlContent);

  // Check that the XInvoice instance has the expected properties
  expect(xinvoice).toBeTruthy();
  expect(xinvoice.from).toBeTruthy();
  expect(xinvoice.to).toBeTruthy();
  expect(xinvoice.items).toBeArray();

  // Create a simple PDF document
  const { PDFDocument } = await import('pdf-lib');
  const pdfDoc = await PDFDocument.create();
  const page = pdfDoc.addPage();
  page.drawText('Test PDF with embedded XML', { x: 50, y: 700 });
  const pdfBytes = await pdfDoc.save();

  // Set the PDF buffer
  xinvoice.pdf = {
    name: 'test-invoice.pdf',
    id: `test-invoice-${Date.now()}`,
    metadata: {
      textExtraction: ''
    },
    buffer: pdfBytes
  };

  // Export as PDF with embedded XML
  const exportedPdf = await xinvoice.exportPdf('facturx');
  expect(exportedPdf).toBeTruthy();
  expect(exportedPdf.buffer).toBeTruthy();

  // Save the exported PDF for inspection
  const testDir = path.join(process.cwd(), 'test', 'output');
  await fs.mkdir(testDir, { recursive: true });
  await fs.writeFile(path.join(testDir, 'test-invoice-with-xml.pdf'), exportedPdf.buffer);

  // Now try to load the PDF back
  const loadedXInvoice = await XInvoice.fromPdf(exportedPdf.buffer);

  // Check that the loaded XInvoice has the expected properties
  expect(loadedXInvoice).toBeTruthy();
  expect(loadedXInvoice.from).toBeTruthy();
  expect(loadedXInvoice.to).toBeTruthy();
  expect(loadedXInvoice.items).toBeArray();

  // Check that key properties are present
  expect(loadedXInvoice.id).toBeTruthy();
  expect(loadedXInvoice.from.name).toBeTruthy();
  expect(loadedXInvoice.to.name).toBeTruthy();

  // Export the loaded invoice back to XML
  const reExportedXml = await loadedXInvoice.exportXml('facturx');
  expect(reExportedXml).toBeTruthy();
  expect(reExportedXml).toInclude('CrossIndustryInvoice');

  // Save the re-exported XML for inspection
  await fs.writeFile(path.join(testDir, 'test-invoice-reextracted.xml'), reExportedXml);
});

/**
 * Recursively finds all PDF files in a directory
 * @param dir Directory to search
 * @returns Array of PDF file paths
 */
async function findPdfFiles(dir: string): Promise<string[]> {
  const files = await fs.readdir(dir, { withFileTypes: true });

  const pdfFiles: string[] = [];

  for (const file of files) {
    const filePath = path.join(dir, file.name);

    if (file.isDirectory()) {
      // Recursively search subdirectories
      const subDirFiles = await findPdfFiles(filePath);
      pdfFiles.push(...subDirFiles);
    } else if (file.name.toLowerCase().endsWith('.pdf')) {
      // Add PDF files to the list
      pdfFiles.push(filePath);
    }
  }

  return pdfFiles;
};

// Test validation of real invoice files
tap.test('XInvoice should validate real invoice files', async () => {
  // Test with a simple CII file
  const xmlPath = path.join(process.cwd(), 'test/assets/corpus/XML-Rechnung/CII/EN16931_Einfach.cii.xml');
  const xmlContent = await fs.readFile(xmlPath, 'utf8');

  // Create XInvoice from XML
  const xinvoice = await XInvoice.fromXml(xmlContent);

  // Validate the XML
  const result = await xinvoice.validate(ValidationLevel.SYNTAX);

  // Check that validation passed
  expect(result.valid).toBeTrue();
  expect(result.errors).toHaveLength(0);
});

// Test with multiple real invoice files
tap.test('XInvoice should handle multiple real invoice files', async () => {
  // Get all CII files
  const ciiDir = path.join(process.cwd(), 'test/assets/corpus/XML-Rechnung/CII');
  const ciiFiles = await fs.readdir(ciiDir);
  const xmlFiles = ciiFiles.filter(file => file.endsWith('.xml'));

  // Test with a subset of files (to keep the test manageable)
  const testFiles = xmlFiles.slice(0, 5);

  // Process each file
  for (const file of testFiles) {
    const xmlPath = path.join(ciiDir, file);
    const xmlContent = await fs.readFile(xmlPath, 'utf8');

    // Create XInvoice from XML
    const xinvoice = await XInvoice.fromXml(xmlContent);

    // Check that the XInvoice instance has the expected properties
    expect(xinvoice).toBeTruthy();
    expect(xinvoice.from).toBeTruthy();
    expect(xinvoice.to).toBeTruthy();

    // Check that the format is detected correctly
    expect(xinvoice.getFormat()).toEqual(InvoiceFormat.FACTURX);

    // Check that the invoice can be exported back to XML
    const exportedXml = await xinvoice.exportXml('facturx');
    expect(exportedXml).toBeTruthy();
    expect(exportedXml).toInclude('CrossIndustryInvoice');
  }
});

// Run the tests
tap.start();