import 'dart:io';

import 'package:commons_core/commons_core.dart';
import 'package:flutter/services.dart' show rootBundle;
import 'package:flutter/widgets.dart';
import 'package:get_it/get_it.dart';
import 'package:path/path.dart' as p;
import 'package:path_provider/path_provider.dart';

import '../data/species_catalog.dart';
import '../data/species_repository.dart';
import '../data/variety_repository.dart';
import '../db/database.dart';
import '../db/encrypted_executor.dart';
import '../i18n/strings.g.dart';
import '../security/secret_store.dart';
import '../security/secure_key_store.dart';
import '../services/export_import_service.dart';
import '../services/file_picker_file_service.dart';
import '../services/file_service.dart';
import '../services/ocr/label_text_extractor.dart';
import '../services/ocr/ocr_language.dart';
import '../services/ocr/tesseract_label_extractor.dart';
import '../services/onboarding_store.dart';
import '../services/recovery_sheet_service.dart';
import '../services/share_catalog_service.dart';

/// The app's service locator. Kept to the composition root — widgets get their
/// repositories from here (or via BlocProvider), never by reaching into it deep
/// in the tree.
final GetIt getIt = GetIt.instance;

/// Wires the encrypted DB, keystore and repositories. Call once from `main`
/// before `runApp`; the DB key must exist before the DB opens.
Future<void> configureDependencies() async {
  final secretStore = FlutterSecretStore();
  final keyStore = SecureKeyStore(store: secretStore);
  final dbKeyHex = await keyStore.databaseKeyHex();
  final rootSeedHex = await keyStore.rootSeedHex();

  final database = AppDatabase(
    openEncryptedExecutor(await _databaseFile(), dbKeyHex),
  );

  // Until real key derivation lands, the node/author id is a stable per-install
  // slice of the root seed. It becomes the user's public key in the social layer.
  final nodeId = rootSeedHex.substring(0, 16);

  // Seed the bundled species catalog (idempotent) before the UI opens.
  final speciesRepository = SpeciesRepository(database, idGen: IdGen());
  await speciesRepository.seedBundled(await loadBundledSpecies());

  final varietyRepository = VarietyRepository(
    database,
    idGen: IdGen(),
    nodeId: nodeId,
  );

  const fileService = FilePickerFileService();

  // OCR label suggestions only where a native Tesseract engine exists; every
  // other platform (desktop, web) degrades to the no-op and hides the button.
  // The language pack(s) follow the user's locale(s) — never a fixed region —
  // limited to what we actually bundle, always unioned with the Latin baseline.
  final LabelTextExtractor labelExtractor =
      (Platform.isAndroid || Platform.isIOS)
      ? TesseractLabelExtractor(language: await _resolveOcrLanguage())
      : const NoOpLabelTextExtractor();

  getIt
    ..registerSingleton<SecureKeyStore>(keyStore)
    ..registerSingleton<AppDatabase>(database)
    ..registerSingleton<SpeciesRepository>(speciesRepository)
    ..registerSingleton<VarietyRepository>(varietyRepository)
    ..registerSingleton<FileService>(fileService)
    ..registerSingleton<LabelTextExtractor>(labelExtractor)
    ..registerSingleton<OnboardingStore>(OnboardingStore(secretStore))
    ..registerSingleton<ExportImportService>(
      ExportImportService(
        repository: varietyRepository,
        files: fileService,
        keys: keyStore,
      ),
    )
    ..registerSingleton<ShareCatalogService>(
      ShareCatalogService(files: fileService),
    )
    ..registerSingleton<RecoverySheetService>(
      RecoverySheetService(files: fileService),
    );
}

Future<File> _databaseFile() async {
  final dir = await getApplicationDocumentsDirectory();
  return File(p.join(dir.path, 'tane_inventory.sqlite'));
}

/// Picks the Tesseract language pack(s) for the current locale(s), limited to
/// the bundled packs listed in `tessdata_config.json`. Prefers the app's chosen
/// language, then the device's ordered locales.
Future<String> _resolveOcrLanguage() async {
  final available = parseAvailableOcrLanguages(
    await rootBundle.loadString('assets/tessdata_config.json'),
  );
  final preferred = <String>[
    LocaleSettings.currentLocale.languageCode,
    for (final l in WidgetsBinding.instance.platformDispatcher.locales)
      l.languageCode,
  ];
  return resolveOcrLanguages(preferredLocales: preferred, available: available);
}
