/**
 * @ai-summary Service for extracting and validating user data import archives
 * @ai-context Extracts tar.gz archives and validates manifest and data files
 */

import * as fsp from 'fs/promises';
import * as path from 'path';
import * as tar from 'tar';
import { logger } from '../../../core/logging/logger';
import { USER_IMPORT_CONFIG, ImportManifest, ImportValidationResult } from './user-import.types';

export class UserImportArchiveService {
  private readonly tempPath: string;

  constructor() {
    this.tempPath = USER_IMPORT_CONFIG.tempPath;
  }

  /**
   * Extracts and validates a user data import archive
   */
  async extractAndValidate(
    archivePath: string,
    userId: string
  ): Promise<ImportValidationResult> {
    const timestamp = Date.now();
    const workDir = path.join(this.tempPath, `import-${userId}-${timestamp}`);

    try {
      // Create working directory
      await fsp.mkdir(workDir, { recursive: true });

      logger.info('Extracting import archive', { userId, archivePath, workDir });

      // Extract tar.gz archive
      await tar.extract({
        file: archivePath,
        cwd: workDir,
        strict: true,
      });

      // Validate extracted structure
      const manifestPath = path.join(workDir, 'manifest.json');
      const dataPath = path.join(workDir, 'data');

      // Check manifest exists
      try {
        await fsp.access(manifestPath);
      } catch {
        return {
          valid: false,
          errors: ['Missing manifest.json in archive'],
        };
      }

      // Parse and validate manifest
      const manifestContent = await fsp.readFile(manifestPath, 'utf-8');
      let manifest: ImportManifest;

      try {
        manifest = JSON.parse(manifestContent);
      } catch (error) {
        return {
          valid: false,
          errors: ['Invalid JSON in manifest.json'],
        };
      }

      // Validate manifest structure
      const manifestErrors = this.validateManifest(manifest);
      if (manifestErrors.length > 0) {
        return {
          valid: false,
          errors: manifestErrors,
        };
      }

      // Check data directory exists
      try {
        await fsp.access(dataPath);
      } catch {
        return {
          valid: false,
          errors: ['Missing data directory in archive'],
        };
      }

      // Validate data files
      const dataFileErrors = await this.validateDataFiles(dataPath);
      if (dataFileErrors.length > 0) {
        return {
          valid: false,
          errors: dataFileErrors,
        };
      }

      logger.info('Import archive validated successfully', { userId, workDir });

      return {
        valid: true,
        errors: [],
        manifest,
        extractedPath: workDir,
      };
    } catch (error) {
      logger.error('Error extracting/validating import archive', {
        userId,
        error: error instanceof Error ? error.message : String(error),
      });

      // Cleanup on error
      try {
        await fsp.rm(workDir, { recursive: true, force: true });
      } catch {
        // Cleanup failed, but already handling error
      }

      return {
        valid: false,
        errors: [`Archive extraction failed: ${error instanceof Error ? error.message : 'Unknown error'}`],
      };
    }
  }

  /**
   * Cleans up extracted archive directory
   */
  async cleanup(extractedPath: string): Promise<void> {
    try {
      await fsp.rm(extractedPath, { recursive: true, force: true });
      logger.info('Cleaned up import work directory', { extractedPath });
    } catch (error) {
      logger.warn('Failed to cleanup import work directory', {
        extractedPath,
        error: error instanceof Error ? error.message : String(error),
      });
    }
  }

  /**
   * Validates manifest structure and required fields
   */
  private validateManifest(manifest: any): string[] {
    const errors: string[] = [];

    if (!manifest.version) {
      errors.push('Manifest missing version field');
    } else if (manifest.version !== USER_IMPORT_CONFIG.supportedVersion) {
      errors.push(`Unsupported archive version: ${manifest.version} (expected ${USER_IMPORT_CONFIG.supportedVersion})`);
    }

    if (!manifest.createdAt) {
      errors.push('Manifest missing createdAt field');
    }

    if (!manifest.userId) {
      errors.push('Manifest missing userId field');
    }

    if (!manifest.contents || typeof manifest.contents !== 'object') {
      errors.push('Manifest missing or invalid contents field');
    } else {
      // Validate contents structure
      const requiredContentFields = [
        'vehicles',
        'fuelLogs',
        'documents',
        'maintenanceRecords',
        'maintenanceSchedules',
      ];
      for (const field of requiredContentFields) {
        if (!manifest.contents[field] || typeof manifest.contents[field].count !== 'number') {
          errors.push(`Manifest contents missing or invalid ${field} field`);
        }
      }
    }

    if (!manifest.files || typeof manifest.files !== 'object') {
      errors.push('Manifest missing or invalid files field');
    }

    if (!Array.isArray(manifest.warnings)) {
      errors.push('Manifest warnings field must be an array');
    }

    return errors;
  }

  /**
   * Validates that all required data files exist and contain valid JSON
   */
  private async validateDataFiles(dataPath: string): Promise<string[]> {
    const errors: string[] = [];
    const requiredFiles = [
      'vehicles.json',
      'fuel-logs.json',
      'documents.json',
      'maintenance-records.json',
      'maintenance-schedules.json',
    ];

    for (const filename of requiredFiles) {
      const filePath = path.join(dataPath, filename);

      try {
        await fsp.access(filePath);
      } catch {
        errors.push(`Missing required data file: ${filename}`);
        continue;
      }

      // Validate JSON structure
      try {
        const content = await fsp.readFile(filePath, 'utf-8');
        JSON.parse(content);
      } catch {
        errors.push(`Invalid JSON in data file: ${filename}`);
      }
    }

    return errors;
  }

  /**
   * Reads and parses a data file from the extracted archive
   */
  async readDataFile<T>(extractedPath: string, filename: string): Promise<T[]> {
    const filePath = path.join(extractedPath, 'data', filename);
    const content = await fsp.readFile(filePath, 'utf-8');
    return JSON.parse(content);
  }
}
