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

// Test other formats corpus (PEPPOL, fatturaPA)
tap.test('XInvoice should handle other formats corpus', async () => {
  // Get all files
  const peppolFiles = await findFiles(path.join(process.cwd(), 'test/assets/corpus/PEPPOL'), '.xml');

  // Skip problematic fatturaPA files
  const fatturapaDir = path.join(process.cwd(), 'test/assets/corpus/fatturaPA');
  const fatturapaFiles = [];

  try {
    // Only test a subset of fatturaPA files to avoid hanging
    const files = await fs.readdir(fatturapaDir, { withFileTypes: true });
    for (const file of files) {
      if (!file.isDirectory() && file.name.endsWith('.xml') && !file.name.includes('Large_Invoice')) {
        fatturapaFiles.push(path.join(fatturapaDir, file.name));
      }
    }
  } catch (error) {
    console.error(`Error reading fatturaPA directory: ${error.message}`);
  }

  // Log the number of files found
  console.log(`Found ${peppolFiles.length} PEPPOL files`);
  console.log(`Found ${fatturapaFiles.length} fatturaPA files`);

  // Test PEPPOL files
  const peppolResults = await testFiles(peppolFiles, InvoiceFormat.UBL);
  console.log(`PEPPOL files: ${peppolResults.success} succeeded, ${peppolResults.fail} failed`);

  // Test fatturaPA files
  const fatturapaResults = await testFiles(fatturapaFiles, InvoiceFormat.UBL);
  console.log(`fatturaPA files: ${fatturapaResults.success} succeeded, ${fatturapaResults.fail} failed`);

  // Check that we have a reasonable success rate
  const totalSuccess = peppolResults.success + fatturapaResults.success;
  const totalFiles = peppolFiles.length + fatturapaFiles.length;
  const successRate = totalSuccess / totalFiles;

  console.log(`Overall success rate: ${(successRate * 100).toFixed(2)}%`);

  // We should have a success rate of at least 50% for these formats
  // They might not be fully supported yet, so we set a lower threshold
  expect(successRate).toBeGreaterThan(0.5);

  // Save the test results to a file
  const testDir = path.join(process.cwd(), 'test', 'output');
  await fs.mkdir(testDir, { recursive: true });

  const testResults = {
    peppol: peppolResults,
    fatturapa: fatturapaResults,
    totalSuccessRate: successRate
  };

  await fs.writeFile(
    path.join(testDir, 'other-formats-corpus-results.json'),
    JSON.stringify(testResults, null, 2)
  );
});

/**
 * Tests a list of XML files and returns the results
 * @param files List of files to test
 * @param expectedFormat Expected format of the files
 * @returns Test results
 */
async function testFiles(files: string[], expectedFormat: InvoiceFormat): Promise<{ success: number, fail: number, details: any[] }> {
  const results = {
    success: 0,
    fail: 0,
    details: [] as any[]
  };

  for (const file of files) {
    try {
      console.log(`Testing file: ${path.basename(file)}`);

      // Read the file with a timeout
      const xmlContent = await Promise.race([
        fs.readFile(file, 'utf8'),
        new Promise<string>((_, reject) => {
          setTimeout(() => reject(new Error('Timeout reading file')), 5000);
        })
      ]);

      // Create XInvoice from XML with a timeout
      const xinvoice = await Promise.race([
        XInvoice.fromXml(xmlContent),
        new Promise<XInvoice>((_, reject) => {
          setTimeout(() => reject(new Error('Timeout processing XML')), 5000);
        })
      ]);

      // Check that the XInvoice instance has the expected properties
      if (xinvoice && xinvoice.from && xinvoice.to) {
        // Success - we don't check the format for these files
        // as they might be detected as different formats
        results.success++;
        results.details.push({
          file,
          success: true,
          format: xinvoice.getFormat(),
          error: null
        });
        console.log(`✅ Success: ${path.basename(file)}`);
      } else {
        // Missing required properties
        results.fail++;
        results.details.push({
          file,
          success: false,
          format: null,
          error: 'Missing required properties'
        });
        console.log(`❌ Failed: ${path.basename(file)} - Missing required properties`);
      }
    } catch (error) {
      // Error processing the file
      results.fail++;
      results.details.push({
        file,
        success: false,
        format: null,
        error: `Error: ${error.message}`
      });
      console.log(`❌ Failed: ${path.basename(file)} - ${error.message}`);
    }
  }

  return results;
}

/**
 * Recursively finds files with a specific extension in a directory
 * @param dir Directory to search
 * @param extension File extension to look for
 * @returns Array of file paths
 */
async function findFiles(dir: string, extension: string): Promise<string[]> {
  try {
    const files = await fs.readdir(dir, { withFileTypes: true });

    const result: string[] = [];

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

      if (file.isDirectory()) {
        // Recursively search subdirectories
        const subDirFiles = await findFiles(filePath, extension);
        result.push(...subDirFiles);
      } else if (file.name.toLowerCase().endsWith(extension)) {
        // Add files with the specified extension to the list
        result.push(filePath);
      }
    }

    return result;
  } catch (error) {
    console.error(`Error finding files in ${dir}:`, error);
    return [];
  }
}

// Run the tests
tap.start();