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 ZUGFeRD v1 and v2 corpus
tap.test('XInvoice should handle ZUGFeRD v1 and v2 corpus', async () => {
  // Get all ZUGFeRD files
  const zugferdV1CorrectFiles = await findFiles(path.join(process.cwd(), 'test/assets/corpus/ZUGFeRDv1/correct'), '.pdf');
  const zugferdV1FailFiles = await findFiles(path.join(process.cwd(), 'test/assets/corpus/ZUGFeRDv1/fail'), '.pdf');
  const zugferdV2CorrectFiles = await findFiles(path.join(process.cwd(), 'test/assets/corpus/ZUGFeRDv2/correct'), '.pdf');
  const zugferdV2FailFiles = await findFiles(path.join(process.cwd(), 'test/assets/corpus/ZUGFeRDv2/fail'), '.pdf');

  // Log the number of files found
  console.log(`Found ${zugferdV1CorrectFiles.length} ZUGFeRD v1 correct files`);
  console.log(`Found ${zugferdV1FailFiles.length} ZUGFeRD v1 fail files`);
  console.log(`Found ${zugferdV2CorrectFiles.length} ZUGFeRD v2 correct files`);
  console.log(`Found ${zugferdV2FailFiles.length} ZUGFeRD v2 fail files`);

  // Test ZUGFeRD v1 correct files
  const v1CorrectResults = await testFiles(zugferdV1CorrectFiles, true);
  console.log(`ZUGFeRD v1 correct files: ${v1CorrectResults.success} succeeded, ${v1CorrectResults.fail} failed`);

  // Test ZUGFeRD v1 fail files
  const v1FailResults = await testFiles(zugferdV1FailFiles, false);
  console.log(`ZUGFeRD v1 fail files: ${v1FailResults.success} succeeded, ${v1FailResults.fail} failed`);

  // Test ZUGFeRD v2 correct files
  const v2CorrectResults = await testFiles(zugferdV2CorrectFiles, true);
  console.log(`ZUGFeRD v2 correct files: ${v2CorrectResults.success} succeeded, ${v2CorrectResults.fail} failed`);

  // Test ZUGFeRD v2 fail files
  const v2FailResults = await testFiles(zugferdV2FailFiles, false);
  console.log(`ZUGFeRD v2 fail files: ${v2FailResults.fail} succeeded, ${v2FailResults.success} failed`);

  // Check that we have a reasonable success rate for correct files
  const totalCorrect = v1CorrectResults.success + v2CorrectResults.success;
  const totalCorrectFiles = zugferdV1CorrectFiles.length + zugferdV2CorrectFiles.length;
  const correctSuccessRate = totalCorrect / totalCorrectFiles;

  console.log(`Overall success rate for correct files: ${(correctSuccessRate * 100).toFixed(2)}%`);

  // We should have a success rate of at least 65% for correct files
  expect(correctSuccessRate).toBeGreaterThan(0.65);

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

  const testResults = {
    zugferdV1Correct: v1CorrectResults,
    zugferdV1Fail: v1FailResults,
    zugferdV2Correct: v2CorrectResults,
    zugferdV2Fail: v2FailResults,
    totalCorrectSuccessRate: correctSuccessRate
  };

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

/**
 * Tests a list of files and returns the results
 * @param files List of files to test
 * @param expectSuccess Whether we expect the files to be successfully processed
 * @returns Test results
 */
async function testFiles(files: string[], expectSuccess: boolean): Promise<{ success: number, fail: number, details: any[] }> {
  const results = {
    success: 0,
    fail: 0,
    details: [] as any[]
  };

  for (const file of files) {
    try {
      // Read the file
      const fileBuffer = await fs.readFile(file);

      // Create XInvoice from PDF
      const xinvoice = await XInvoice.fromPdf(fileBuffer);

      // Check that the XInvoice instance has the expected properties
      if (xinvoice && xinvoice.from && xinvoice.to && xinvoice.items) {
        // Check that the format is detected correctly
        const format = xinvoice.getFormat();
        const isZugferd = [InvoiceFormat.ZUGFERD, InvoiceFormat.FACTURX, InvoiceFormat.CII].includes(format);

        if (isZugferd) {
          // Try to export the invoice to XML
          try {
            const exportedXml = await xinvoice.exportXml('facturx');

            if (exportedXml && exportedXml.includes('CrossIndustryInvoice')) {
              // Success
              results.success++;
              results.details.push({
                file,
                success: true,
                format,
                error: null
              });
            } else {
              // Failed to export valid XML
              results.fail++;
              results.details.push({
                file,
                success: false,
                format,
                error: 'Failed to export valid XML'
              });
            }
          } catch (exportError) {
            // Failed to export XML
            results.fail++;
            results.details.push({
              file,
              success: false,
              format,
              error: `Export error: ${exportError.message}`
            });
          }
        } else {
          // Wrong format detected
          results.fail++;
          results.details.push({
            file,
            success: false,
            format,
            error: `Wrong format detected: ${format}`
          });
        }
      } else {
        // Missing required properties
        results.fail++;
        results.details.push({
          file,
          success: false,
          format: null,
          error: 'Missing required properties'
        });
      }
    } catch (error) {
      // If we expect success, this is a failure
      // If we expect failure, this is a success
      if (expectSuccess) {
        results.fail++;
        results.details.push({
          file,
          success: false,
          format: null,
          error: `Error: ${error.message}`
        });
      } else {
        results.success++;
        results.details.push({
          file,
          success: true,
          format: null,
          error: `Expected error: ${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();
