import 'dart:ui';

import 'package:aves/model/entry/sort.dart';
import 'package:aves/services/common/channel.dart';
import 'package:aves/services/common/services.dart';
import 'package:aves/theme/format.dart';
import 'package:collection/collection.dart';
import 'package:flutter/services.dart';
import 'package:flutter/widgets.dart';
import 'package:intl/date_symbol_data_local.dart';

class GlobalSearch {
  static const _platform = AvesMethodChannel('deckers.thibault/aves/global_search');

  static Future<void> registerCallback() async {
    try {
      await _platform.invokeMethod('registerCallback', <String, dynamic>{
        // callback needs to be annotated with `@pragma('vm:entry-point')` to work in release mode
        'callbackHandle': PluginUtilities.getCallbackHandle(_init)?.toRawHandle(),
      });
    } on PlatformException catch (e, stack) {
      await reportService.recordError(e, stack);
    }
  }
}

@pragma('vm:entry-point')
Future<void> _init() async {
  WidgetsFlutterBinding.ensureInitialized();

  // service initialization for path context, database
  initPlatformServices();
  await localMediaDb.init();

  // `intl` initialization for date formatting
  await initializeDateFormatting();

  const _channel = AvesMethodChannel('deckers.thibault/aves/global_search_background');
  _channel.setMethodCallHandler((call) async {
    switch (call.method) {
      case 'getSuggestions':
        return await _getSuggestions(call.arguments);
      default:
        throw PlatformException(code: 'not-implemented', message: 'failed to handle method=${call.method}');
    }
  });
  await _channel.invokeMethod('initialized');
}

Future<List<Map<String, String?>>> _getSuggestions(dynamic args) async {
  final suggestions = <Map<String, String?>>[];
  if (args is Map) {
    final query = args['query'];
    final locale = args['locale'];
    final use24hour = args['use24hour'];
    debugPrint('getSuggestions query=$query, locale=$locale use24hour=$use24hour');

    if (query is String && locale is String) {
      final entries = (await localMediaDb.searchLiveEntries(query, limit: 9)).toList();
      final catalogMetadata = await localMediaDb.loadCatalogMetadataById(entries.map((entry) => entry.id).toSet());
      catalogMetadata.forEach((metadata) => entries.firstWhereOrNull((entry) => entry.id == metadata.id)?.catalogMetadata = metadata);
      entries.sort(AvesEntrySort.compareByDate);

      suggestions.addAll(
        entries.map((entry) {
          final date = entry.bestDate;
          return {
            'data': entry.uri,
            'mimeType': entry.mimeType,
            'title': entry.bestTitle,
            'subtitle': date != null ? formatDateTime(date, locale, use24hour) : null,
            'iconUri': entry.uri,
          };
        }),
      );
    }
  }
  return suggestions;
}
