import 'dart:convert';
import 'dart:typed_data';
import 'package:http/http.dart' as http;

const _stickerServerUrl = 'https://stickers.steggi-matrix.work';

class GifFavoriteService {
  // ── GIF Favorites cache ─────────────────────────────────────────────────
  static final List<Map<String, dynamic>> _cache = [];
  static bool _loaded = false;

  // ── Sticker Favorites cache ──────────────────────────────────────────────
  static final List<Map<String, dynamic>> _stickerCache = [];
  static bool _stickerLoaded = false;

  static Future<List<Map<String, dynamic>>> load() async {
    if (_loaded) return _cache;
    try {
      final res = await http.get(Uri.parse('$_stickerServerUrl/gif-favorites'));
      final data = jsonDecode(res.body) as List;
      _cache
        ..clear()
        ..addAll(data.cast<Map<String, dynamic>>());
      _loaded = true;
    } catch (_) {}
    return _cache;
  }

  static List<Map<String, dynamic>> get cache => List.unmodifiable(_cache);

  /// True if the given https:// URL is already a favorite.
  static bool isFavorite(String url) =>
      _cache.any((f) => f['url'] == url);

  /// True if a favorite was created from this mxc:// source URL.
  static bool isFavoriteByMxc(String mxcUrl) =>
      _cache.any((f) => f['mxc_source'] == mxcUrl);

  /// Upload raw GIF/WebP bytes to the Pi server.
  /// Returns the public https:// URL on success, null on failure.
  static Future<String?> uploadToPi(Uint8List bytes, String mimeType) async {
    try {
      final res = await http.post(
        Uri.parse('$_stickerServerUrl/gif-file-upload'),
        headers: {'Content-Type': mimeType},
        body: bytes,
      );
      if (res.statusCode == 200) {
        final data = jsonDecode(res.body);
        return data['url'] as String?;
      }
    } catch (_) {}
    return null;
  }

  /// Toggle a plain external GIF (Giphy etc.).
  static Future<void> toggle(
    String url,
    String previewUrl,
    String title,
  ) async {
    final removing = isFavorite(url);
    if (removing) {
      _cache.removeWhere((f) => f['url'] == url);
    } else {
      _cache.add({'url': url, 'preview_url': previewUrl, 'title': title});
    }
    try {
      await http.post(
        Uri.parse('$_stickerServerUrl/gif-favorites'),
        headers: {'Content-Type': 'application/json'},
        body: jsonEncode({
          'action': removing ? 'remove' : 'add',
          'url': url,
          'preview_url': previewUrl,
          'title': title,
        }),
      );
    } catch (_) {
      // Revert on error
      if (removing) {
        _cache.add({'url': url, 'preview_url': previewUrl, 'title': title});
      } else {
        _cache.removeWhere((f) => f['url'] == url);
      }
    }
  }

  /// Add a favorite that was uploaded from an mxc:// source.
  /// [url] is the Pi-hosted https:// URL; [mxcSource] is the original mxc:// URL.
  static Future<void> addWithMxcSource(
    String url,
    String previewUrl,
    String title,
    String mxcSource,
  ) async {
    final entry = {
      'url': url,
      'preview_url': previewUrl,
      'title': title,
      'mxc_source': mxcSource,
    };
    _cache.add(entry);
    try {
      await http.post(
        Uri.parse('$_stickerServerUrl/gif-favorites'),
        headers: {'Content-Type': 'application/json'},
        body: jsonEncode({
          'action': 'add',
          'url': url,
          'preview_url': previewUrl,
          'title': title,
          'mxc_source': mxcSource,
        }),
      );
    } catch (_) {
      _cache.removeWhere((f) => f['mxc_source'] == mxcSource);
    }
  }

  /// Remove a favorite that was originally added from an mxc:// source.
  static Future<void> removeByMxc(String mxcSource) async {
    _cache.removeWhere((f) => f['mxc_source'] == mxcSource);
    try {
      await http.post(
        Uri.parse('$_stickerServerUrl/gif-favorites'),
        headers: {'Content-Type': 'application/json'},
        body: jsonEncode({
          'action': 'remove',
          'url': '',
          'mxc_source': mxcSource,
        }),
      );
    } catch (_) {}
  }

  /// Extracts the URL logic from a matrix event to easily toggle favoriting globally.
  static Future<bool> toggleEventFavorites(dynamic event) async {
    await load();
    String rawUrl = '';
    final url = event.content['url'] as String?;
    if (url != null && url.isNotEmpty) {
      rawUrl = url;
    } else {
      final fileObj = event.content['file'];
      if (fileObj is Map) {
        final fileUrl = fileObj['url'];
        if (fileUrl is String && fileUrl.isNotEmpty) rawUrl = fileUrl;
      }
    }

    if (rawUrl.isEmpty) return false;

    final u = Uri.tryParse(rawUrl);
    final isExternal = u != null && (u.scheme == 'https' || u.scheme == 'http');
    final title = event.content['body'] as String? ?? 'Sticker';
    final isFav = isExternal ? isFavorite(rawUrl) : isFavoriteByMxc(rawUrl);

    if (isExternal) {
      await toggle(rawUrl, rawUrl, title);
      return true;
    } else if (rawUrl.startsWith('mxc://')) {
      if (isFav) {
        await removeByMxc(rawUrl);
        return true;
      } else {
        try {
          final matrixFile = await event.downloadAndDecryptAttachment(getThumbnail: false);
          final mimeType = event.infoMap['mimetype'] as String? ?? 'image/webp';
          final piUrl = await uploadToPi(matrixFile.bytes, mimeType);
          if (piUrl != null) {
            await addWithMxcSource(piUrl, piUrl, title, rawUrl);
            return true;
          }
        } catch (e) {
          return false;
        }
      }
    }
    return false;
  }

  // ── Sticker Favorites ────────────────────────────────────────────────────

  /// Load sticker favorites from the server into the sticker cache.
  static Future<List<Map<String, dynamic>>> loadStickers() async {
    if (_stickerLoaded) return _stickerCache;
    try {
      final res = await http.get(Uri.parse('$_stickerServerUrl/sticker-favorites'));
      final data = jsonDecode(res.body) as List;
      _stickerCache
        ..clear()
        ..addAll(data.cast<Map<String, dynamic>>());
      _stickerLoaded = true;
    } catch (_) {}
    return _stickerCache;
  }

  static List<Map<String, dynamic>> get stickerCache => List.unmodifiable(_stickerCache);

  /// All user-created stickers (saved from images). Shown in "Meine Sticker".
  static List<Map<String, dynamic>> get userStickerItems =>
      List.unmodifiable(
        _stickerCache.where((s) => s['user_sticker'] == true).toList(),
      );

  /// Explicitly favorited stickers. Includes:
  ///  - User stickers with favorited == true
  ///  - Pack sticker favorites (entries without user_sticker == true)
  static List<Map<String, dynamic>> get stickerFavoritesItems =>
      List.unmodifiable(
        _stickerCache.where((s) {
          if (s['user_sticker'] == true) return s['favorited'] == true;
          return true;
        }).toList(),
      );

  /// Remove a sticker from the local cache immediately (without a server round-trip).
  /// Call this after successfully posting a remove action to the server.
  static void removeSticker(String mxcUrl) {
    _stickerCache.removeWhere((s) => s['mxc_source'] == mxcUrl);
  }

  /// True if this mxc:// URL is in the effective favorites list.
  static bool isStickerFavorite(String mxcUrl) =>
      stickerFavoritesItems.any((s) => s['mxc_source'] == mxcUrl);

  /// Toggle the favorited flag on a user sticker. Returns true on success.
  static Future<bool> setUserStickerFavorited(String mxcSource, bool favorited) async {
    final idx = _stickerCache.indexWhere((s) => s['mxc_source'] == mxcSource);
    if (idx == -1) return false;
    _stickerCache[idx]['favorited'] = favorited;
    try {
      await http.post(
        Uri.parse('$_stickerServerUrl/sticker-favorites'),
        headers: {'Content-Type': 'application/json'},
        body: jsonEncode({
          'action': 'set_favorited',
          'mxc_source': mxcSource,
          'favorited': favorited,
        }),
      );
      return true;
    } catch (_) {
      _stickerCache[idx]['favorited'] = !favorited;
      return false;
    }
  }

  /// Add or remove any image/sticker event from sticker favorites.
  /// Handles both unencrypted (content['url']) and encrypted (content['file']['url']) events.
  /// For encrypted events, re-uploads the decrypted bytes to Matrix so the
  /// stored mxc:// URL can be sent as a sticker without decryption keys.
  static Future<bool> addImageAsStickerFavorite(dynamic event) async {
    await loadStickers();

    // Resolve original mxc URL (used as deduplication key)
    String rawMxc = event.content['url'] as String? ?? '';
    final bool isEncrypted = event.content['file'] != null;
    if (rawMxc.isEmpty && isEncrypted) {
      final fileObj = event.content['file'];
      if (fileObj is Map) rawMxc = fileObj['url'] as String? ?? '';
    }
    if (rawMxc.isEmpty) return false;

    final title = event.content['body'] as String? ?? 'Sticker';

    // Check if ALREADY in cache (by mxc_source), regardless of favorited status.
    // This prevents duplicates when user taps "Als Sticker speichern" on an already-saved sticker.
    final existingIdx = _stickerCache.indexWhere((s) => s['mxc_source'] == rawMxc);
    if (existingIdx != -1) {
      // Toggle: remove the existing user sticker
      final backup = Map<String, dynamic>.from(_stickerCache[existingIdx]);
      _stickerCache.removeAt(existingIdx);
      try {
        await http.post(
          Uri.parse('$_stickerServerUrl/sticker-favorites'),
          headers: {'Content-Type': 'application/json'},
          body: jsonEncode({'action': 'remove', 'mxc_source': rawMxc}),
        );
        return true;
      } catch (_) {
        _stickerCache.insert(existingIdx, backup);
        return false;
      }
    } else {
      try {
        final matrixFile = await event.downloadAndDecryptAttachment(getThumbnail: false);
        final mimeType = (event.infoMap['mimetype'] as String?) ?? 'image/png';

        // For encrypted events the original mxc:// points to ciphertext.
        // Re-upload the decrypted bytes to get a plain, sendable mxc:// URL.
        String stickerMxc;
        if (isEncrypted) {
          final dynamic uploadedUri = await event.room.client.uploadContent(
            matrixFile.bytes,
            filename: 'sticker',
            contentType: mimeType,
          );
          stickerMxc = uploadedUri.toString();
        } else {
          stickerMxc = rawMxc;
        }

        // Also upload to Pi server for the picker preview thumbnail
        final piUrl = await uploadToPi(matrixFile.bytes, mimeType);
        if (piUrl == null) return false;

        final entry = {
          'mxc_source': rawMxc,  // deduplication key (original encrypted or plain mxc)
          'mxc': stickerMxc,     // sendable unencrypted mxc:// URL
          'url': piUrl,           // Pi preview URL shown in the picker
          'title': title,
          'mimetype': mimeType,
          'user_sticker': true,   // created from an image, shown in "Meine Sticker"
          'favorited': false,     // not in favorites until explicitly hearted
        };
        _stickerCache.add(entry);
        await http.post(
          Uri.parse('$_stickerServerUrl/sticker-favorites'),
          headers: {'Content-Type': 'application/json'},
          body: jsonEncode({'action': 'add', ...entry}),
        );
        return true;
      } catch (_) {
        _stickerCache.removeWhere((s) => s['mxc_source'] == rawMxc);
        return false;
      }
    }
  }

  /// Add or remove a pack sticker from favorites using its already-accessible
  /// thumbnail URL — no download or re-upload needed.
  static Future<bool> togglePackStickerFavorite({
    required String mxcUrl,
    required String thumbUrl,
    required String title,
    String mimetype = 'image/webp',
  }) async {
    await loadStickers();

    final isFav = isStickerFavorite(mxcUrl);

    if (isFav) {
      _stickerCache.removeWhere((s) => s['mxc_source'] == mxcUrl);
      try {
        await http.post(
          Uri.parse('$_stickerServerUrl/sticker-favorites'),
          headers: {'Content-Type': 'application/json'},
          body: jsonEncode({'action': 'remove', 'mxc_source': mxcUrl}),
        );
        return true;
      } catch (_) {
        _stickerCache.add({'mxc_source': mxcUrl, 'mxc': mxcUrl, 'url': thumbUrl, 'title': title});
        return false;
      }
    } else {
      final entry = {
        'mxc_source': mxcUrl,
        'mxc': mxcUrl,
        'url': thumbUrl,
        'title': title,
        'mimetype': mimetype,
        'user_sticker': false,
        'favorited': false,
      };
      _stickerCache.add(entry);
      try {
        await http.post(
          Uri.parse('$_stickerServerUrl/sticker-favorites'),
          headers: {'Content-Type': 'application/json'},
          body: jsonEncode({'action': 'add', ...entry}),
        );
        return true;
      } catch (_) {
        _stickerCache.removeWhere((s) => s['mxc_source'] == mxcUrl);
        return false;
      }
    }
  }

  /// Toggle a sticker event into/out of the sticker favorites.
  /// For user-created stickers, only the favorited flag is toggled.
  /// For pack stickers, the sticker is added/removed from the favorites list.
  static Future<bool> toggleStickerFavorite(dynamic event) async {
    await loadStickers();
    final rawMxc = event.content['url'] as String? ?? '';
    if (rawMxc.isEmpty) return false;
    final title = event.content['body'] as String? ?? 'Sticker';

    // If it's a user-created sticker, toggle its favorited flag only
    final userIdx = _stickerCache.indexWhere(
      (s) => s['mxc_source'] == rawMxc && s['user_sticker'] == true,
    );
    if (userIdx != -1) {
      final currentFav = _stickerCache[userIdx]['favorited'] == true;
      return setUserStickerFavorited(rawMxc, !currentFav);
    }

    final isFav = isStickerFavorite(rawMxc);

    if (isFav) {
      _stickerCache.removeWhere((s) => s['mxc_source'] == rawMxc);
      try {
        await http.post(
          Uri.parse('$_stickerServerUrl/sticker-favorites'),
          headers: {'Content-Type': 'application/json'},
          body: jsonEncode({'action': 'remove', 'mxc_source': rawMxc}),
        );
        return true;
      } catch (_) {
        _stickerCache.add({'mxc_source': rawMxc, 'title': title});
        return false;
      }
    } else {
      // Upload thumbnail bytes to Pi for preview
      try {
        final matrixFile = await event.downloadAndDecryptAttachment(getThumbnail: false);
        final mimeType = event.infoMap['mimetype'] as String? ?? 'image/webp';
        final piUrl = await uploadToPi(matrixFile.bytes, mimeType);
        if (piUrl == null) return false;
        final entry = {
          'mxc_source': rawMxc,
          'mxc': rawMxc,
          'url': piUrl,
          'title': title,
          'mimetype': mimeType,
          'user_sticker': false,
          'favorited': false,
        };
        _stickerCache.add(entry);
        await http.post(
          Uri.parse('$_stickerServerUrl/sticker-favorites'),
          headers: {'Content-Type': 'application/json'},
          body: jsonEncode({'action': 'add', ...entry}),
        );
        return true;
      } catch (_) {
        _stickerCache.removeWhere((s) => s['mxc_source'] == rawMxc);
        return false;
      }
    }
  }
}
