import 'dart:convert';
import 'dart:io';
import 'package:flutter/foundation.dart';
import 'package:flutter/services.dart';
import 'package:path_provider/path_provider.dart';
import 'package:archive/archive_io.dart';
import '../core/database.dart';
import '../core/preferences.dart';
import '../models/trip.dart';
import '../models/photo.dart';
import '../models/place.dart';
import '../models/participant.dart';
import 'html_generator.dart';


class ExportService {
  static final ExportService instance = ExportService._init();
  ExportService._init();

  static const _channel = MethodChannel('cz.coolcode.live_vacation_memories/media');

  /// Exports a trip into a desktop-compatible ZIP archive.
  /// Returns the absolute path of the generated ZIP file.
  Future<String> exportTripToZip(Trip trip, Function(double progress, String status)? onProgress) async {
    final db = DatabaseHelper.instance;

    onProgress?.call(0.0, 'Načítám data z databáze...');
    
    // 1. Fetch data
    final List<Photo> photos = await db.getPhotosForTrip(trip.id);
    final List<Place> places = await db.getPlacesForTrip(trip.id);
    final List<Participant> participants = await db.getParticipantsForTrip(trip.id);


    // 2. Prepare JSONs
    final manifest = {
      'version': '1.0.0',
      'exportTimeUtc': DateTime.now().millisecondsSinceEpoch,
      'deviceId': AppPreferences.deviceId,
      'ownerName': AppPreferences.displayName,
    };

    final tripData = trip.toMap();
    final photosData = photos.map((p) => p.toMap()).toList();
    final placesData = places.map((p) => p.toMap()).toList();
    final participantsData = participants.map((p) => p.toMap()).toList();

    onProgress?.call(0.1, 'Připravuji soubory...');

    // Create a temporary export directory
    final tempDir = await getTemporaryDirectory();
    final exportDir = Directory('${tempDir.path}/export_${trip.id}');
    if (await exportDir.exists()) {
      await exportDir.delete(recursive: true);
    }
    await exportDir.create(recursive: true);

    // Create subfolders
    final originalsDir = Directory('${exportDir.path}/originals');
    await originalsDir.create();
    final thumbnailsDir = Directory('${exportDir.path}/thumbnails');
    await thumbnailsDir.create();

    // Write JSON manifest files
    await File('${exportDir.path}/manifest.json').writeAsString(jsonEncode(manifest));
    await File('${exportDir.path}/trip.json').writeAsString(jsonEncode(tripData));
    await File('${exportDir.path}/photos.json').writeAsString(jsonEncode(photosData));
    await File('${exportDir.path}/places.json').writeAsString(jsonEncode(placesData));
    await File('${exportDir.path}/participants.json').writeAsString(jsonEncode(participantsData));

    // 3. Process Photos
    final totalPhotos = photos.length;
    for (var i = 0; i < totalPhotos; i++) {
      final Photo photo = photos[i];
      final progressBase = 0.1 + (i / totalPhotos) * 0.7;

      
      onProgress?.call(progressBase, 'Zpracovávám fotku ${i + 1} z $totalPhotos: ${photo.displayName}');

      try {
        Uint8List? originalBytes;
        Uint8List? thumbnailBytes;

        // Load original bytes
        if (photo.contentUri.startsWith('content://')) {
          originalBytes = await _channel.invokeMethod<Uint8List>('getPhotoBytes', {'uri': photo.contentUri});
        } else {
          final file = File(photo.contentUri);
          if (await file.exists()) {
            originalBytes = await file.readAsBytes();
          }
        }

        // Load thumbnail bytes
        if (photo.contentUri.startsWith('content://')) {
          thumbnailBytes = await _channel.invokeMethod<Uint8List>('getThumbnail', {
            'uri': photo.contentUri,
            'width': 200,
            'height': 200,
          });
        } else {
          // If local filesystem, read original file and use it or let Flutter generate thumbnail.
          // Since it's local, we can just copy the original, or since native getThumbnail fails on file paths,
          // we can just use the original bytes or resize it in Dart if needed, or simply copy the original bytes for the thumbnail path as well!
          thumbnailBytes = originalBytes;
        }

        // Save original to subfolder
        if (originalBytes != null) {
          await File('${originalsDir.path}/${photo.id}.jpg').writeAsBytes(originalBytes);
        }

        // Save thumbnail to subfolder
        if (thumbnailBytes != null) {
          await File('${thumbnailsDir.path}/${photo.id}.jpg').writeAsBytes(thumbnailBytes);
        }
      } catch (e) {
        // Skip photo if failed but log error
        debugPrint('Chyba při exportu fotky ${photo.id}: $e');
      }
    }

    onProgress?.call(0.85, 'Vytvářím ZIP archiv...');

    // 4. Compress to ZIP using archive package
    final sanitizedTripName = trip.name.replaceAll(RegExp(r'[<>:"/\\|?*]'), '_').trim();
    
    Directory? outputDir;
    if (Platform.isAndroid) {
      final pubDownload = Directory('/storage/emulated/0/Download');
      if (await pubDownload.exists()) {
        outputDir = pubDownload;
      } else {
        final sdcardDownload = Directory('/sdcard/Download');
        if (await sdcardDownload.exists()) {
          outputDir = sdcardDownload;
        }
      }
    }

    if (outputDir == null) {
      final docDir = await getApplicationDocumentsDirectory();
      outputDir = Directory('${docDir.path}/exports');
      if (!await outputDir.exists()) {
        await outputDir.create(recursive: true);
      }
    }

    final zipFile = File('${outputDir.path}/$sanitizedTripName.zip');
    if (await zipFile.exists()) {
      await zipFile.delete();
    }

    // Zip encoder running synchronously
    final encoder = ZipFileEncoder();
    encoder.create(zipFile.path);
    await encoder.addDirectory(exportDir);
    encoder.close();

    // 5. Clean up temp folder
    await exportDir.delete(recursive: true);

    onProgress?.call(1.0, 'Export úspěšně dokončen!');
    return zipFile.path;
  }

  /// Exports the interactive HTML map and copy of photos to Downloads/LVM/TripName
  Future<String> exportInteractiveMap(Trip trip, List<Place> places, List<Photo> photos, Function(double progress, String status)? onProgress, {String? password}) async {
    onProgress?.call(0.0, 'Příprava exportu interaktivní mapy...');

    // 1. Determine Output Directory: Downloads/LVM/TripName
    final sanitizedTripName = trip.name.replaceAll(RegExp(r'[<>:"/\\|?*]'), '_').trim();
    Directory? downloadsDir;
    if (Platform.isAndroid) {
      final pubDownload = Directory('/storage/emulated/0/Download');
      if (await pubDownload.exists()) {
        downloadsDir = pubDownload;
      } else {
        final sdcardDownload = Directory('/sdcard/Download');
        if (await sdcardDownload.exists()) {
          downloadsDir = sdcardDownload;
        }
      }
    }

    if (downloadsDir == null) {
      final docDir = await getApplicationDocumentsDirectory();
      downloadsDir = Directory('${docDir.path}/exports');
    }

    final lvmDir = Directory('${downloadsDir.path}/LVM');
    if (!await lvmDir.exists()) {
      await lvmDir.create(recursive: true);
    }

    final targetDir = Directory('${lvmDir.path}/$sanitizedTripName');
    if (await targetDir.exists()) {
      await targetDir.delete(recursive: true);
    }
    await targetDir.create(recursive: true);

    final fotkyDir = Directory('${targetDir.path}/Fotky');
    await fotkyDir.create(recursive: true);

    // Helper for safe filenames
    String getSafeFileName(Photo photo) {
      final name = photo.displayName.trim();
      if (name.isEmpty) return '${photo.id}.jpg';
      if (!name.contains('.')) return '$name.jpg';
      return name;
    }

    // 2. Generate and write index.html
    onProgress?.call(0.1, 'Generuji index.html...');
    final html = HtmlGenerator.generateHtml(trip, places, photos, forExport: true, password: password);
    final htmlFile = File('${targetDir.path}/index.html');
    await htmlFile.writeAsString(html);

    // 3. Copy/Export all photos
    final totalPhotos = photos.length;
    for (var i = 0; i < totalPhotos; i++) {
      final Photo photo = photos[i];
      final progressBase = 0.2 + (i / totalPhotos) * 0.8;
      final safeName = getSafeFileName(photo);

      onProgress?.call(progressBase, 'Kopíruji fotku ${i + 1} z $totalPhotos: $safeName');

      try {
        Uint8List? originalBytes;
        if (photo.contentUri.startsWith('content://')) {
          originalBytes = await _channel.invokeMethod<Uint8List>('getPhotoBytes', {'uri': photo.contentUri});
        } else {
          final file = File(photo.contentUri);
          if (await file.exists()) {
            originalBytes = await file.readAsBytes();
          }
        }

        if (originalBytes != null) {
          final targetPhotoFile = File('${fotkyDir.path}/$safeName');
          await targetPhotoFile.writeAsBytes(originalBytes);
        }
      } catch (e) {
        debugPrint('Chyba při exportu fotky $safeName: $e');
      }
    }

    onProgress?.call(1.0, 'Export mapy úspěšně dokončen!');
    return targetDir.path;
  }

  /// Exports the interactive HTML map and copy of photos as a ZIP archive for Netlify drag-and-drop.
  /// Returns the absolute path of the generated ZIP file.
  Future<String> exportInteractiveMapAsZip(Trip trip, List<Place> places, List<Photo> photos, Function(double progress, String status)? onProgress, {String? password}) async {
    onProgress?.call(0.0, 'Připravuji webový export pro Netlify...');

    // 1. Generate the standard interactive map folder first
    final folderPath = await exportInteractiveMap(trip, places, photos, (prog, stat) {
      // Scale progress from 0.0 to 0.8
      onProgress?.call(prog * 0.8, stat);
    }, password: password);

    onProgress?.call(0.85, 'Vytvářím ZIP archiv pro Netlify...');

    final sanitizedTripName = trip.name.replaceAll(RegExp(r'[<>:"/\\|?*]'), '_').trim();
    final sourceDir = Directory(folderPath);
    
    Directory? downloadsDir;
    if (Platform.isAndroid) {
      final pubDownload = Directory('/storage/emulated/0/Download');
      if (await pubDownload.exists()) {
        downloadsDir = pubDownload;
      } else {
        final sdcardDownload = Directory('/sdcard/Download');
        if (await sdcardDownload.exists()) {
          downloadsDir = sdcardDownload;
        }
      }
    }

    if (downloadsDir == null) {
      final docDir = await getApplicationDocumentsDirectory();
      downloadsDir = Directory('${docDir.path}/exports');
    }

    final lvmDir = Directory('${downloadsDir.path}/LVM');
    final zipFile = File('${lvmDir.path}/${sanitizedTripName}_web.zip');
    if (await zipFile.exists()) {
      await zipFile.delete();
    }

    // 2. Compress the exported folder to ZIP
    final encoder = ZipFileEncoder();
    encoder.create(zipFile.path);
    await encoder.addDirectory(sourceDir);
    encoder.close();

    onProgress?.call(1.0, 'Export pro Netlify dokončen!');
    return zipFile.path;
  }
}
