import 'dart:convert';
import 'package:flutter/services.dart';
import '../models/torrent_info.dart';

/// Data classes for torrent metadata (matching Kotlin side)

/// Базовая информация из magnet-ссылки
class MagnetBasicInfo {
  final String name;
  final String infoHash;
  final List<String> trackers;
  final int totalSize;

  MagnetBasicInfo({
    required this.name,
    required this.infoHash,
    required this.trackers,
    this.totalSize = 0,
  });

  factory MagnetBasicInfo.fromJson(Map<String, dynamic> json) {
    return MagnetBasicInfo(
      name: json['name'] as String,
      infoHash: json['infoHash'] as String,
      trackers: List<String>.from(json['trackers'] as List),
      totalSize: json['totalSize'] as int? ?? 0,
    );
  }

  Map<String, dynamic> toJson() {
    return {
      'name': name,
      'infoHash': infoHash,
      'trackers': trackers,
      'totalSize': totalSize,
    };
  }
}

/// Информация о файле в торренте
class FileInfo {
  final String name;
  final String path;
  final int size;
  final int index;
  final String extension;
  final bool isVideo;
  final bool isAudio;
  final bool isImage;
  final bool isDocument;
  final bool selected;

  FileInfo({
    required this.name,
    required this.path,
    required this.size,
    required this.index,
    this.extension = '',
    this.isVideo = false,
    this.isAudio = false,
    this.isImage = false,
    this.isDocument = false,
    this.selected = false,
  });

  factory FileInfo.fromJson(Map<String, dynamic> json) {
    return FileInfo(
      name: json['name'] as String,
      path: json['path'] as String,
      size: json['size'] as int,
      index: json['index'] as int,
      extension: json['extension'] as String? ?? '',
      isVideo: json['isVideo'] as bool? ?? false,
      isAudio: json['isAudio'] as bool? ?? false,
      isImage: json['isImage'] as bool? ?? false,
      isDocument: json['isDocument'] as bool? ?? false,
      selected: json['selected'] as bool? ?? false,
    );
  }

  Map<String, dynamic> toJson() {
    return {
      'name': name,
      'path': path,
      'size': size,
      'index': index,
      'extension': extension,
      'isVideo': isVideo,
      'isAudio': isAudio,
      'isImage': isImage,
      'isDocument': isDocument,
      'selected': selected,
    };
  }

  FileInfo copyWith({
    String? name,
    String? path,
    int? size,
    int? index,
    String? extension,
    bool? isVideo,
    bool? isAudio,
    bool? isImage,
    bool? isDocument,
    bool? selected,
  }) {
    return FileInfo(
      name: name ?? this.name,
      path: path ?? this.path,
      size: size ?? this.size,
      index: index ?? this.index,
      extension: extension ?? this.extension,
      isVideo: isVideo ?? this.isVideo,
      isAudio: isAudio ?? this.isAudio,
      isImage: isImage ?? this.isImage,
      isDocument: isDocument ?? this.isDocument,
      selected: selected ?? this.selected,
    );
  }
}

/// Узел директории
class DirectoryNode {
  final String name;
  final String path;
  final List<FileInfo> files;
  final List<DirectoryNode> subdirectories;
  final int totalSize;
  final int fileCount;

  DirectoryNode({
    required this.name,
    required this.path,
    required this.files,
    required this.subdirectories,
    required this.totalSize,
    required this.fileCount,
  });

  factory DirectoryNode.fromJson(Map<String, dynamic> json) {
    return DirectoryNode(
      name: json['name'] as String,
      path: json['path'] as String,
      files: (json['files'] as List)
          .map((file) => FileInfo.fromJson(file as Map<String, dynamic>))
          .toList(),
      subdirectories: (json['subdirectories'] as List)
          .map((dir) => DirectoryNode.fromJson(dir as Map<String, dynamic>))
          .toList(),
      totalSize: json['totalSize'] as int,
      fileCount: json['fileCount'] as int,
    );
  }
}

/// Структура файлов торрента
class FileStructure {
  final DirectoryNode rootDirectory;
  final int totalFiles;
  final Map<String, int> filesByType;

  FileStructure({
    required this.rootDirectory,
    required this.totalFiles,
    required this.filesByType,
  });

  factory FileStructure.fromJson(Map<String, dynamic> json) {
    return FileStructure(
      rootDirectory: DirectoryNode.fromJson(json['rootDirectory'] as Map<String, dynamic>),
      totalFiles: json['totalFiles'] as int,
      filesByType: Map<String, int>.from(json['filesByType'] as Map),
    );
  }
}

/// Полные метаданные торрента
class TorrentMetadataFull {
  final String name;
  final String infoHash;
  final int totalSize;
  final int pieceLength;
  final int numPieces;
  final FileStructure fileStructure;
  final List<String> trackers;
  final int creationDate;
  final String comment;
  final String createdBy;

  TorrentMetadataFull({
    required this.name,
    required this.infoHash,
    required this.totalSize,
    required this.pieceLength,
    required this.numPieces,
    required this.fileStructure,
    required this.trackers,
    required this.creationDate,
    required this.comment,
    required this.createdBy,
  });

  factory TorrentMetadataFull.fromJson(Map<String, dynamic> json) {
    return TorrentMetadataFull(
      name: json['name'] as String,
      infoHash: json['infoHash'] as String,
      totalSize: json['totalSize'] as int,
      pieceLength: json['pieceLength'] as int,
      numPieces: json['numPieces'] as int,
      fileStructure: FileStructure.fromJson(json['fileStructure'] as Map<String, dynamic>),
      trackers: List<String>.from(json['trackers'] as List),
      creationDate: json['creationDate'] as int,
      comment: json['comment'] as String,
      createdBy: json['createdBy'] as String,
    );
  }

  /// Получить плоский список всех файлов
  List<FileInfo> getAllFiles() {
    final List<FileInfo> allFiles = [];
    _collectFiles(fileStructure.rootDirectory, allFiles);
    return allFiles;
  }

  void _collectFiles(DirectoryNode directory, List<FileInfo> result) {
    result.addAll(directory.files);
    for (final subdir in directory.subdirectories) {
      _collectFiles(subdir, result);
    }
  }
}

class TorrentFileInfo {
  final String path;
  final int size;
  final bool selected;

  TorrentFileInfo({
    required this.path,
    required this.size,
    this.selected = false,
  });

  factory TorrentFileInfo.fromJson(Map<String, dynamic> json) {
    return TorrentFileInfo(
      path: json['path'] as String,
      size: json['size'] as int,
      selected: json['selected'] as bool? ?? false,
    );
  }

  Map<String, dynamic> toJson() {
    return {
      'path': path,
      'size': size,
      'selected': selected,
    };
  }

  TorrentFileInfo copyWith({
    String? path,
    int? size,
    bool? selected,
  }) {
    return TorrentFileInfo(
      path: path ?? this.path,
      size: size ?? this.size,
      selected: selected ?? this.selected,
    );
  }
}

class TorrentMetadata {
  final String name;
  final int totalSize;
  final List<TorrentFileInfo> files;
  final String infoHash;

  TorrentMetadata({
    required this.name,
    required this.totalSize,
    required this.files,
    required this.infoHash,
  });

  factory TorrentMetadata.fromJson(Map<String, dynamic> json) {
    return TorrentMetadata(
      name: json['name'] as String,
      totalSize: json['totalSize'] as int,
      files: (json['files'] as List)
          .map((file) => TorrentFileInfo.fromJson(file as Map<String, dynamic>))
          .toList(),
      infoHash: json['infoHash'] as String,
    );
  }

  Map<String, dynamic> toJson() {
    return {
      'name': name,
      'totalSize': totalSize,
      'files': files.map((file) => file.toJson()).toList(),
      'infoHash': infoHash,
    };
  }
}

class DownloadProgress {
  final String infoHash;
  final double progress;
  final int downloadRate;
  final int uploadRate;
  final int numSeeds;
  final int numPeers;
  final String state;

  DownloadProgress({
    required this.infoHash,
    required this.progress,
    required this.downloadRate,
    required this.uploadRate,
    required this.numSeeds,
    required this.numPeers,
    required this.state,
  });

  factory DownloadProgress.fromJson(Map<String, dynamic> json) {
    return DownloadProgress(
      infoHash: json['infoHash'] as String,
      progress: (json['progress'] as num).toDouble(),
      downloadRate: json['downloadRate'] as int,
      uploadRate: json['uploadRate'] as int,
      numSeeds: json['numSeeds'] as int,
      numPeers: json['numPeers'] as int,
      state: json['state'] as String,
    );
  }
}

/// Platform service for torrent operations using jlibtorrent on Android
class TorrentPlatformService {
  static const MethodChannel _channel = MethodChannel('com.neo.neomovies_mobile/torrent');

  /// Add torrent from magnet URI and start downloading
  static Future<String> addTorrent({
    required String magnetUri,
    String? savePath,
  }) async {
    try {
      final String infoHash = await _channel.invokeMethod('addTorrent', {
        'magnetUri': magnetUri,
        'savePath': savePath ?? '/storage/emulated/0/Download/NeoMovies',
      });
      
      return infoHash;
    } on PlatformException catch (e) {
      throw Exception('Failed to add torrent: ${e.message}');
    }
  }

  /// Get all torrents
  static Future<List<DownloadProgress>> getAllDownloads() async {
    try {
      final String result = await _channel.invokeMethod('getTorrents');
      
      final List<dynamic> jsonList = jsonDecode(result);
      return jsonList.map((json) {
        final data = json as Map<String, dynamic>;
        return DownloadProgress(
          infoHash: data['infoHash'] as String,
          progress: (data['progress'] as num).toDouble(),
          downloadRate: data['downloadSpeed'] as int,
          uploadRate: data['uploadSpeed'] as int,
          numSeeds: data['numSeeds'] as int,
          numPeers: data['numPeers'] as int,
          state: data['state'] as String,
        );
      }).toList();
    } on PlatformException catch (e) {
      throw Exception('Failed to get all downloads: ${e.message}');
    } catch (e) {
      throw Exception('Failed to parse downloads: $e');
    }
  }

  /// Get single torrent info
  static Future<TorrentInfo?> getTorrent(String infoHash) async {
    try {
      final String result = await _channel.invokeMethod('getTorrent', {
        'infoHash': infoHash,
      });
      
      final Map<String, dynamic> json = jsonDecode(result);
      return TorrentInfo.fromAndroidJson(json);
    } on PlatformException catch (e) {
      if (e.code == 'NOT_FOUND') return null;
      throw Exception('Failed to get torrent: ${e.message}');
    } catch (e) {
      throw Exception('Failed to parse torrent: $e');
    }
  }

  /// Get download progress for a torrent
  static Future<DownloadProgress?> getDownloadProgress(String infoHash) async {
    try {
      final torrentInfo = await getTorrent(infoHash);
      if (torrentInfo == null) return null;
      
      return DownloadProgress(
        infoHash: torrentInfo.infoHash,
        progress: torrentInfo.progress,
        downloadRate: torrentInfo.downloadSpeed,
        uploadRate: torrentInfo.uploadSpeed,
        numSeeds: torrentInfo.numSeeds,
        numPeers: torrentInfo.numPeers,
        state: torrentInfo.state,
      );
    } catch (e) {
      return null;
    }
  }

  /// Pause download
  static Future<bool> pauseDownload(String infoHash) async {
    try {
      final bool result = await _channel.invokeMethod('pauseTorrent', {
        'infoHash': infoHash,
      });
      
      return result;
    } on PlatformException catch (e) {
      throw Exception('Failed to pause download: ${e.message}');
    }
  }

  /// Resume download
  static Future<bool> resumeDownload(String infoHash) async {
    try {
      final bool result = await _channel.invokeMethod('resumeTorrent', {
        'infoHash': infoHash,
      });
      
      return result;
    } on PlatformException catch (e) {
      throw Exception('Failed to resume download: ${e.message}');
    }
  }

  /// Cancel and remove download
  static Future<bool> cancelDownload(String infoHash) async {
    try {
      final bool result = await _channel.invokeMethod('removeTorrent', {
        'infoHash': infoHash,
        'deleteFiles': true,
      });
      
      return result;
    } on PlatformException catch (e) {
      throw Exception('Failed to cancel download: ${e.message}');
    }
  }

  /// Set file priority
  static Future<bool> setFilePriority(String infoHash, int fileIndex, FilePriority priority) async {
    try {
      final bool result = await _channel.invokeMethod('setFilePriority', {
        'infoHash': infoHash,
        'fileIndex': fileIndex,
        'priority': priority.value,
      });
      
      return result;
    } on PlatformException catch (e) {
      throw Exception('Failed to set file priority: ${e.message}');
    }
  }

  /// Start downloading selected files from torrent
  static Future<String> startDownload({
    required String magnetLink,
    required List<int> selectedFiles,
    String? downloadPath,
  }) async {
    try {
      // First add the torrent
      final String infoHash = await addTorrent(
        magnetUri: magnetLink,
        savePath: downloadPath,
      );
      
      // Wait for metadata to be received
      await Future.delayed(const Duration(seconds: 2));
      
      // Set file priorities
      final torrentInfo = await getTorrent(infoHash);
      if (torrentInfo != null) {
        for (int i = 0; i < torrentInfo.files.length; i++) {
          final priority = selectedFiles.contains(i) 
            ? FilePriority.NORMAL 
            : FilePriority.DONT_DOWNLOAD;
          await setFilePriority(infoHash, i, priority);
        }
      }
      
      return infoHash;
    } catch (e) {
      throw Exception('Failed to start download: $e');
    }
  }

  // Legacy methods for compatibility with existing code

  /// Get torrent metadata from magnet link (legacy method)
  static Future<TorrentMetadata> getTorrentMetadata(String magnetLink) async {
    try {
      // This is a simplified implementation that adds the torrent and gets metadata
      final infoHash = await addTorrent(magnetUri: magnetLink);
      await Future.delayed(const Duration(seconds: 3)); // Wait for metadata
      
      final torrentInfo = await getTorrent(infoHash);
      if (torrentInfo == null) {
        throw Exception('Failed to get torrent metadata');
      }
      
      return TorrentMetadata(
        name: torrentInfo.name,
        totalSize: torrentInfo.totalSize,
        files: torrentInfo.files.map((file) => TorrentFileInfo(
          path: file.path,
          size: file.size,
          selected: file.priority > FilePriority.DONT_DOWNLOAD,
        )).toList(),
        infoHash: torrentInfo.infoHash,
      );
    } catch (e) {
      throw Exception('Failed to get torrent metadata: $e');
    }
  }

  /// Получить базовую информацию из magnet-ссылки (legacy)
  static Future<MagnetBasicInfo> parseMagnetBasicInfo(String magnetUri) async {
    try {
      // Parse magnet URI manually since Android implementation doesn't have this
      final uri = Uri.parse(magnetUri);
      final params = uri.queryParameters;
      
      return MagnetBasicInfo(
        name: params['dn'] ?? 'Unknown',
        infoHash: params['xt']?.replaceFirst('urn:btih:', '') ?? '',
        trackers: params['tr'] != null ? [params['tr']!] : [],
        totalSize: 0,
      );
    } catch (e) {
      throw Exception('Failed to parse magnet basic info: $e');
    }
  }

  /// Получить полные метаданные торрента (legacy)
  static Future<TorrentMetadataFull> fetchFullMetadata(String magnetUri) async {
    try {
      final basicInfo = await parseMagnetBasicInfo(magnetUri);
      final metadata = await getTorrentMetadata(magnetUri);
      
      return TorrentMetadataFull(
        name: metadata.name,
        infoHash: metadata.infoHash,
        totalSize: metadata.totalSize,
        pieceLength: 0,
        numPieces: 0,
        fileStructure: FileStructure(
          rootDirectory: DirectoryNode(
            name: metadata.name,
            path: '/',
            files: metadata.files.map((file) => FileInfo(
              name: file.path.split('/').last,
              path: file.path,
              size: file.size,
              index: metadata.files.indexOf(file),
            )).toList(),
            subdirectories: [],
            totalSize: metadata.totalSize,
            fileCount: metadata.files.length,
          ),
          totalFiles: metadata.files.length,
          filesByType: {'video': metadata.files.length},
        ),
        trackers: basicInfo.trackers,
        creationDate: 0,
        comment: '',
        createdBy: '',
      );
    } catch (e) {
      throw Exception('Failed to fetch full metadata: $e');
    }
  }
}
