// ABOUTME: File handling utilities for safe backup and restore operations
// ABOUTME: Provides secure file I/O, validation, sharing, and cleanup for backup functionality
import 'dart:io';
import 'dart:convert';
import 'dart:typed_data';
import 'package:flutter/foundation.dart';
import 'package:crypto/crypto.dart';
import 'package:path/path.dart' as path;
import 'package:path_provider/path_provider.dart';
import 'package:share_plus/share_plus.dart';
import 'package:file_picker/file_picker.dart';
import '../models/backup/backup_metadata.dart';
import 'package:permission_handler/permission_handler.dart';

/// Utility class for file operations related to backup and restore
class FileUtils {
  /// Directory names
  static const String backupDirName = 'backups';
  static const String tempDirName = 'temp';
  
  /// File extensions
  static const String backupExtension = '.json';
  static const String csvExtension = '.csv';
  static const String jsonExtension = '.json';

  /// Maximum file size for import (10MB)
  static const int maxImportFileSize = 10 * 1024 * 1024;

  /// Get filename from path
  static String getFileName(String filePath) {
    return path.basename(filePath);
  }

  /// Get file extension from path
  static String getFileExtension(String filePath) {
    final lastDot = filePath.lastIndexOf('.');
    if (lastDot == -1) return '';
    return filePath.substring(lastDot);
  }

  /// Format file size in human-readable format
  static String formatFileSize(int bytes) {
    if (bytes < 1024) return '$bytes B';
    if (bytes < 1024 * 1024) return '${(bytes / 1024).toStringAsFixed(1)} KB';
    if (bytes < 1024 * 1024 * 1024) return '${(bytes / (1024 * 1024)).toStringAsFixed(1)} MB';
    return '${(bytes / (1024 * 1024 * 1024)).toStringAsFixed(1)} GB';
  }

  /// Get the app documents directory
  static Future<Directory> getAppDocumentsDirectory() async {
    return await getApplicationDocumentsDirectory();
  }

  /// Get the backup directory, creating it if necessary
  static Future<Directory> getBackupDirectory() async {
    final appDir = await getAppDocumentsDirectory();
    final backupDir = Directory(path.join(appDir.path, backupDirName));
    
    if (!await backupDir.exists()) {
      await backupDir.create(recursive: true);
    }
    
    return backupDir;
  }

  /// Get the temporary directory for file operations
  static Future<Directory> getTempDirectory() async {
    final appDir = await getAppDocumentsDirectory();
    final tempDir = Directory(path.join(appDir.path, tempDirName));
    
    if (!await tempDir.exists()) {
      await tempDir.create(recursive: true);
    }
    
    return tempDir;
  }

  /// Show system save dialog with file content and return the saved file
  static Future<File?> saveFileWithDialog({
    required String suggestedName,
    required List<int> bytes,
    String? dialogTitle,
    List<String>? allowedExtensions,
  }) async {
    try {
      // Use FilePicker to show save dialog with bytes
      String? outputFile = await FilePicker.platform.saveFile(
        dialogTitle: dialogTitle ?? 'Save File',
        fileName: suggestedName,
        type: allowedExtensions != null ? FileType.custom : FileType.any,
        allowedExtensions: allowedExtensions,
        bytes: Uint8List.fromList(bytes),
      );
      
      if (outputFile != null) {
        return File(outputFile);
      }
      return null;
    } catch (e) {
      debugPrint('Error showing save dialog: $e');
      return null;
    }
  }

  /// Create and save CSV file using system save dialog
  static Future<File?> createCsvFileWithDialog({
    required String filename,
    required List<String> headers,
    required List<List<String>> rows,
    String? dialogTitle,
  }) async {
    try {
      final buffer = StringBuffer();
      
      // Add headers
      buffer.writeln(headers.join(','));
      
      // Add data rows
      for (final row in rows) {
        // Escape CSV values
        final escapedRow = row.map((value) {
          if (value.contains(',') || value.contains('"') || value.contains('\n')) {
            return '"${value.replaceAll('"', '""')}"';
          }
          return value;
        });
        buffer.writeln(escapedRow.join(','));
      }

      // Convert to bytes
      final csvString = buffer.toString();
      final bytes = utf8.encode(csvString);
      
      // Show save dialog with content
      return await saveFileWithDialog(
        suggestedName: filename,
        bytes: bytes,
        dialogTitle: dialogTitle ?? 'Save CSV File',
        allowedExtensions: ['csv'],
      );
      
    } catch (e) {
      debugPrint('Error creating CSV file with dialog: $e');
      rethrow;
    }
  }

  /// Generate a unique backup filename
  static String generateBackupFilename({String? prefix}) {
    final now = DateTime.now();
    final timestamp = '${now.year}${now.month.toString().padLeft(2, '0')}${now.day.toString().padLeft(2, '0')}_'
                     '${now.hour.toString().padLeft(2, '0')}${now.minute.toString().padLeft(2, '0')}';
    final prefixPart = prefix != null ? '${prefix}_' : '';
    return '${prefixPart}healthtracker_backup_$timestamp$backupExtension';
  }

  /// Write backup data to file
  static Future<File> writeBackupFile(
    Map<String, dynamic> backupData, {
    String? filename,
    Directory? directory,
  }) async {
    final backupDir = directory ?? await getBackupDirectory();
    final fileName = filename ?? generateBackupFilename();
    final file = File(path.join(backupDir.path, fileName));

    // Convert to JSON string
    final jsonString = JsonEncoder.withIndent('  ').convert(backupData);
    
    // Write to file
    await file.writeAsString(jsonString, encoding: utf8);
    
    return file;
  }

  /// Read backup data from file
  static Future<Map<String, dynamic>> readBackupFile(File file) async {
    if (!await file.exists()) {
      throw FileSystemException('Backup file does not exist', file.path);
    }

    // Check file size
    final stat = await file.stat();
    if (stat.size > maxImportFileSize) {
      throw FileSystemException(
        'File too large: ${formatFileSize(stat.size)} (max: ${formatFileSize(maxImportFileSize)})',
        file.path,
      );
    }

    // Read and parse JSON
    final jsonString = await file.readAsString(encoding: utf8);
    final data = json.decode(jsonString) as Map<String, dynamic>;
    
    return data;
  }

  /// Calculate checksum for data integrity verification
  static String calculateDataChecksum(Map<String, dynamic> data) {
    // Convert data to canonical JSON string and hash it
    final jsonString = json.encode(data);
    final bytes = utf8.encode(jsonString);
    final digest = sha256.convert(bytes);
    return digest.toString();
  }

  /// Validate backup file format and integrity
  static Future<ValidationResult> validateBackupFile(File file) async {
    final errors = <String>[];
    final warnings = <String>[];

    try {
      // Check file exists and is readable
      if (!await file.exists()) {
        errors.add('File does not exist');
        return ValidationResult(isValid: false, errors: errors, warnings: warnings);
      }

      // Check file size
      final stat = await file.stat();
      if (stat.size == 0) {
        errors.add('File is empty');
        return ValidationResult(isValid: false, errors: errors, warnings: warnings);
      }

      if (stat.size > maxImportFileSize) {
        errors.add('File too large: ${formatFileSize(stat.size)} (max: ${formatFileSize(maxImportFileSize)})');
        return ValidationResult(isValid: false, errors: errors, warnings: warnings);
      }

      // Read and parse JSON
      final data = await readBackupFile(file);

      // Validate structure
      if (!data.containsKey('metadata')) {
        errors.add('Missing metadata section');
      } else {
        try {
          final metadata = BackupMetadata.fromJson(data['metadata']);
          
          // Check compatibility
          if (!metadata.isCompatible) {
            warnings.add('Backup version ${metadata.version} may not be fully compatible');
          }

          // Check age
          final age = DateTime.now().difference(metadata.createdAt);
          if (age.inDays > 365) {
            warnings.add('Backup is over a year old (${metadata.ageDescription})');
          }
        } catch (e) {
          errors.add('Invalid metadata format: $e');
        }
      }

      if (!data.containsKey('data')) {
        errors.add('Missing data section');
      }

      // Validate checksum if present
      if (data.containsKey('metadata') && data['metadata']['checksum'] != null) {
        final expectedChecksum = data['metadata']['checksum'];
        final actualChecksum = calculateDataChecksum(data['data'] ?? {});
        
        if (expectedChecksum != actualChecksum) {
          errors.add('Data integrity check failed - file may be corrupted');
        }
      }

    } catch (e) {
      errors.add('Failed to read or parse file: $e');
    }

    return ValidationResult(
      isValid: errors.isEmpty,
      errors: errors,
      warnings: warnings,
    );
  }

  /// List all backup files in backup directory
  static Future<List<File>> listBackupFiles() async {
    try {
      final directory = await getBackupDirectory();
      if (!await directory.exists()) {
        return [];
      }
      
      final files = <File>[];
      await for (final entity in directory.list()) {
        if (entity is File) {
          final ext = getFileExtension(entity.path).toLowerCase();
          if (ext == '.json' || ext == '.csv') {
            files.add(entity);
          }
        }
      }
      
      // Sort by modification time, newest first
      files.sort((a, b) => b.statSync().modified.compareTo(a.statSync().modified));
      return files;
    } catch (e) {
      throw FileSystemException('Failed to list backup files: $e');
    }
  }

  /// Share a file using platform sharing
  static Future<void> shareFile(File file) async {
    try {
      await Share.shareXFiles(
        [XFile(file.path)],
        text: 'Health Tracker Backup - ${getFileName(file.path)}',
      );
    } catch (e) {
      throw FileSystemException('Failed to share file: $e');
    }
  }

  /// Create CSV file from data
  static Future<File> createCsvFile({
    required String filename,
    required List<String> headers,
    required List<List<String>> rows,
    Directory? directory,
  }) async {
    final dir = directory ?? await getTempDirectory();
    final file = File(path.join(dir.path, filename));

    final buffer = StringBuffer();
    
    // Add headers
    buffer.writeln(headers.join(','));
    
    // Add data rows
    for (final row in rows) {
      // Escape CSV values
      final escapedRow = row.map((value) {
        if (value.contains(',') || value.contains('"') || value.contains('\n')) {
          return '"${value.replaceAll('"', '""')}"';
        }
        return value;
      });
      buffer.writeln(escapedRow.join(','));
    }

    await file.writeAsString(buffer.toString(), encoding: utf8);
    return file;
  }

  /// Get file information
  static Future<FileInfo> getFileInfo(File file) async {
    if (!await file.exists()) {
      throw FileSystemException('File does not exist', file.path);
    }

    final stat = await file.stat();
    final name = path.basename(file.path);
    
    return FileInfo(
      name: name,
      path: file.path,
      size: stat.size,
      modified: stat.modified,
      sizeFormatted: formatFileSize(stat.size),
    );
  }
}

/// Result of file validation
class ValidationResult {
  final bool isValid;
  final List<String> errors;
  final List<String> warnings;

  const ValidationResult({
    required this.isValid,
    required this.errors,
    required this.warnings,
  });

  bool get hasWarnings => warnings.isNotEmpty;
}

/// File information
class FileInfo {
  final String name;
  final String path;
  final int size;
  final DateTime modified;
  final String sizeFormatted;

  const FileInfo({
    required this.name,
    required this.path,
    required this.size,
    required this.modified,
    required this.sizeFormatted,
  });
}
