import 'package:dynamic_color/dynamic_color.dart';
import 'package:flutter/material.dart';
import 'package:flutter_localizations/flutter_localizations.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:google_fonts/google_fonts.dart';
import 'package:pdfrx/pdfrx.dart';
import 'package:shared_preferences/shared_preferences.dart';

import 'editor/persistence/sidecar_flush_observer.dart';
import 'editor/pdf/pen_capture_region.dart';
import 'l10n/app_localizations.dart';
import 'providers/settings_provider.dart';
import 'screens/home_screen.dart';
import 'screens/vault_setup_screen.dart';
import 'services/database_service.dart';
import 'services/vault_service.dart';
import 'storage/sqlite_to_sidecar_migrator.dart';

Future<void> main() async {
  // Kind-aware binding (extends WidgetsFlutterBinding) must be the active
  // binding before runApp so the M1 spike's PenCaptureRegion can gate
  // hit-testing by pointer kind. Safe for the rest of the app: with no pen
  // region mounted it behaves exactly like the default binding.
  PenCaptureBinding.ensureInitialized();
  // pdfrx native engine init (required before any PdfViewer is built).
  pdfrxFlutterInitialize();

  // Ensure DB is ready before the app starts so providers can use it eagerly
  await DatabaseService.getInstance();

  // Initialize SharedPreferences
  await SharedPreferences.getInstance();

  runApp(const ProviderScope(child: BadNoteApp()));
}

class BadNoteApp extends ConsumerStatefulWidget {
  const BadNoteApp({super.key});

  @override
  ConsumerState<BadNoteApp> createState() => _BadNoteAppState();
}

class _BadNoteAppState extends ConsumerState<BadNoteApp> {
  // Phase 6 / §F.3: flush any open sidecar repos when the app is suspended or
  // closed so the last strokes are never lost to an OS kill. Lives for the whole
  // app lifetime (attached here, detached on app teardown).
  final SidecarFlushObserver _flushObserver = SidecarFlushObserver();

  @override
  void initState() {
    super.initState();
    _flushObserver.attach();
  }

  @override
  void dispose() {
    _flushObserver.detach();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    final settings = ref.watch(settingsProvider);

    // Material You: prefer the OS dynamic color (Windows/Android system accent);
    // fall back to the user's seed color when the platform provides none.
    return DynamicColorBuilder(
      builder: (lightDynamic, darkDynamic) {
        final lightScheme = lightDynamic?.harmonized() ??
            ColorScheme.fromSeed(
              seedColor: settings.colorSchemeSeed,
              brightness: Brightness.light,
            );
        final darkScheme = darkDynamic?.harmonized() ??
            ColorScheme.fromSeed(
              seedColor: settings.colorSchemeSeed,
              brightness: Brightness.dark,
            );
        return MaterialApp(
          title: 'BadNote',
          themeMode: settings.themeMode,
          theme: _theme(lightScheme),
          darkTheme: _theme(darkScheme),
          // i18n: follows the OS language (en / zh) via the system locale.
          localizationsDelegates: const [
            AppLocalizations.delegate,
            GlobalMaterialLocalizations.delegate,
            GlobalWidgetsLocalizations.delegate,
            GlobalCupertinoLocalizations.delegate,
          ],
          supportedLocales: AppLocalizations.supportedLocales,
          home: const VaultGate(),
        );
      },
    );
  }

  ThemeData _theme(ColorScheme scheme) => ThemeData(
        colorScheme: scheme,
        useMaterial3: true,
        textTheme: GoogleFonts.interTextTheme(
          ThemeData(brightness: scheme.brightness).textTheme,
        ),
      );
}

/// Startup gate: shows [HomeScreen] only once a valid vault root folder has been
/// chosen. If none is set — or the saved folder no longer exists — it shows
/// [VaultSetupScreen] first (re-prompting on a missing folder rather than
/// silently scattering data elsewhere). Phase 0: records the vault path only;
/// editors still use SQLite.
class VaultGate extends StatefulWidget {
  const VaultGate({super.key});

  @override
  State<VaultGate> createState() => _VaultGateState();
}

class _VaultGateState extends State<VaultGate> {
  VaultService? _vault;
  bool _valid = false;
  bool _hadStoredPath = false;
  bool _loading = true;
  bool _migrating = false;

  @override
  void initState() {
    super.initState();
    _check();
  }

  Future<void> _check() async {
    final vault = await VaultService.getInstance();
    final valid = await vault.vaultRootValid();
    if (!mounted) return;
    setState(() {
      _vault = vault;
      _valid = valid;
      // A stored-but-invalid path means the chosen folder went missing.
      _hadStoredPath = (vault.vaultRoot?.isNotEmpty ?? false);
      _loading = false;
    });
    if (valid) await _maybeMigrate(vault);
  }

  /// Run the one-time SQLite→sidecar migration ONCE per vault (Phase 5, §B).
  /// Gated on [VaultService.vaultMigrationDone]; a fresh install (no legacy DB)
  /// is a fast no-op. The live DB is first reopened at the vault cache location
  /// so post-migration reads hit the new index, never the renamed legacy file.
  Future<void> _maybeMigrate(VaultService vault) async {
    // Move the live cache DB to the vault location now that the root is valid.
    await DatabaseService.reopen();
    if (vault.vaultMigrationDone) return;
    if (mounted) setState(() => _migrating = true);
    try {
      await SqliteToSidecarMigrator(vault).run();
      await vault.setVaultMigrationDone();
    } catch (_) {
      // A failed migration leaves the legacy DB intact (it is only renamed to
      // `.premigration` after a successful pass) and the flag unset, so the
      // next launch retries. Never block the user from reaching the app.
    }
    if (mounted) setState(() => _migrating = false);
  }

  @override
  Widget build(BuildContext context) {
    if (_loading) {
      return const Scaffold(
        body: Center(child: CircularProgressIndicator()),
      );
    }
    if (_migrating) {
      return const Scaffold(
        body: Center(
          child: Column(
            mainAxisSize: MainAxisSize.min,
            children: [
              CircularProgressIndicator(),
              SizedBox(height: 16),
              Text('Migrating your notebooks…'),
            ],
          ),
        ),
      );
    }
    if (_valid) return const HomeScreen();
    return VaultSetupScreen(
      vaultService: _vault!,
      missing: _hadStoredPath,
      onVaultReady: () async {
        if (_vault != null) await _maybeMigrate(_vault!);
        if (mounted) setState(() => _valid = true);
      },
    );
  }
}
