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 validation of corpus files
tap.test('XInvoice should validate corpus files correctly', async () => {
  // Get a subset of files for validation testing
  const zugferdV2CorrectFiles = await findFiles(path.join(process.cwd(), 'test/assets/corpus/ZUGFeRDv2/correct'), '.pdf', 5);
  const zugferdV2FailFiles = await findFiles(path.join(process.cwd(), 'test/assets/corpus/ZUGFeRDv2/fail'), '.pdf', 5);
  const ciiFiles = await findFiles(path.join(process.cwd(), 'test/assets/corpus/XML-Rechnung/CII'), '.xml', 5);
  const ublFiles = await findFiles(path.join(process.cwd(), 'test/assets/corpus/XML-Rechnung/UBL'), '.xml', 5);

  // Log the number of files found
  console.log(`Found ${zugferdV2CorrectFiles.length} ZUGFeRD v2 correct files for validation`);
  console.log(`Found ${zugferdV2FailFiles.length} ZUGFeRD v2 fail files for validation`);
  console.log(`Found ${ciiFiles.length} CII files for validation`);
  console.log(`Found ${ublFiles.length} UBL files for validation`);

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

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

  // Test CII files
  const ciiResults = await testValidation(ciiFiles, false, true);
  console.log(`CII files validation: ${ciiResults.success} succeeded, ${ciiResults.fail} failed`);

  // Test UBL files
  const ublResults = await testValidation(ublFiles, false, true);
  console.log(`UBL files validation: ${ublResults.success} succeeded, ${ublResults.fail} failed`);

  // Check that we have a reasonable success rate for correct files
  const totalCorrectSuccess = zugferdV2CorrectResults.success + ciiResults.success + ublResults.success;
  const totalCorrectFiles = zugferdV2CorrectFiles.length + ciiFiles.length + ublFiles.length;
  const correctSuccessRate = totalCorrectSuccess / totalCorrectFiles;

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

  // We should have a success rate of at least 60% for correct files
  // Note: This is lower than ideal because we haven't implemented the XRechnung validator yet
  expect(correctSuccessRate).toBeGreaterThan(0.6);

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

  const testResults = {
    zugferdV2Correct: zugferdV2CorrectResults,
    zugferdV2Fail: zugferdV2FailResults,
    cii: ciiResults,
    ubl: ublResults,
    totalCorrectSuccessRate: correctSuccessRate
  };

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

/**
 * Tests validation of files and returns the results
 * @param files List of files to test
 * @param isPdf Whether the files are PDFs
 * @param expectValid Whether we expect the files to be valid
 * @returns Test results
 */
async function testValidation(files: string[], isPdf: boolean, expectValid: boolean): Promise<{ success: number, fail: number, details: any[] }> {
  const results = {
    success: 0,
    fail: 0,
    details: [] as any[]
  };

  for (const file of files) {
    try {
      // Create XInvoice from file
      let xinvoice: XInvoice;

      if (isPdf) {
        const fileBuffer = await fs.readFile(file);
        xinvoice = await XInvoice.fromPdf(fileBuffer);
      } else {
        const xmlContent = await fs.readFile(file, 'utf8');
        xinvoice = await XInvoice.fromXml(xmlContent);
      }

      // Validate the invoice
      const validationResult = await xinvoice.validate(ValidationLevel.SYNTAX);

      // Check if the validation result matches our expectation
      if (validationResult.valid === expectValid) {
        // Success
        results.success++;
        results.details.push({
          file,
          success: true,
          valid: validationResult.valid,
          errors: validationResult.errors,
          error: null
        });
      } else {
        // Validation result doesn't match expectation
        results.fail++;
        results.details.push({
          file,
          success: false,
          valid: validationResult.valid,
          errors: validationResult.errors,
          error: `Validation result (${validationResult.valid}) doesn't match expectation (${expectValid})`
        });
      }
    } catch (error) {
      // Error processing the file
      results.fail++;
      results.details.push({
        file,
        success: false,
        valid: null,
        errors: null,
        error: `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
 * @param limit Maximum number of files to return
 * @returns Array of file paths
 */
async function findFiles(dir: string, extension: string, limit?: number): Promise<string[]> {
  try {
    const files = await fs.readdir(dir, { withFileTypes: true });

    const result: string[] = [];

    for (const file of files) {
      if (limit && result.length >= limit) {
        break;
      }

      const filePath = path.join(dir, file.name);

      if (file.isDirectory()) {
        // Recursively search subdirectories
        const remainingLimit = limit ? limit - result.length : undefined;
        const subDirFiles = await findFiles(filePath, extension, remainingLimit);
        result.push(...subDirFiles);

        if (limit && result.length >= limit) {
          break;
        }
      } 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();
