import 'package:commons_core/commons_core.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';

import '../data/export_import/inventory_snapshot.dart';
import '../di/injector.dart';
import '../i18n/strings.g.dart';
import '../services/export_import_service.dart';
import '../services/recovery_sheet_service.dart';

/// The backup actions shown in Settings. The primary pair — save/restore a
/// full copy — comes first, then the recovery sheet, then the spreadsheet
/// list export/import as a secondary convenience. File formats and crypto are
/// implementation details and never surface in the UI. Restore/import asks
/// for confirmation first (it merges into the live inventory), then reports
/// what happened in a SnackBar.
class BackupSection extends StatelessWidget {
  const BackupSection({this.service, this.sheet, super.key});

  /// Injectable for widget tests; the app resolves them lazily (on tap) from
  /// the service locator so merely mounting Settings needs no DI setup.
  final ExportImportService? service;
  final RecoverySheetService? sheet;

  ExportImportService get _service => service ?? getIt<ExportImportService>();
  RecoverySheetService get _sheet => sheet ?? getIt<RecoverySheetService>();

  @override
  Widget build(BuildContext context) {
    final t = context.t;
    return Column(
      children: [
        ListTile(
          leading: const Icon(Icons.save_alt_outlined),
          title: Text(t.backup.exportJson),
          subtitle: Text(t.backup.exportJsonSubtitle),
          onTap: () => _runExport(context, () => _service.exportBackup()),
        ),
        ListTile(
          leading: const Icon(Icons.settings_backup_restore_outlined),
          title: Text(t.backup.importJson),
          subtitle: Text(t.backup.importJsonSubtitle),
          onTap: () => _runRestore(context),
        ),
        ListTile(
          key: const Key('backup.recovery'),
          leading: const Icon(Icons.qr_code_2_outlined),
          title: Text(t.backup.recoveryTitle),
          subtitle: Text(t.backup.recoverySubtitle),
          onTap: () => _showRecoverySheet(context),
        ),
        const Divider(),
        ListTile(
          leading: const Icon(Icons.table_view_outlined),
          title: Text(t.backup.exportCsv),
          subtitle: Text(t.backup.exportCsvSubtitle),
          onTap: () => _runExport(context, () => _service.exportCsv()),
        ),
        ListTile(
          leading: const Icon(Icons.playlist_add_outlined),
          title: Text(t.backup.importCsv),
          subtitle: Text(t.backup.importCsvSubtitle),
          onTap: () => _runImportCsv(context),
        ),
      ],
    );
  }

  Future<void> _runExport(
    BuildContext context,
    Future<bool> Function() export,
  ) async {
    final t = context.t;
    final messenger = ScaffoldMessenger.of(context);
    try {
      final saved = await export();
      _show(messenger, saved ? t.backup.exportSaved : t.backup.cancelled);
    } on Object {
      _show(messenger, t.backup.failed);
    }
  }

  /// Shows this device's recovery code and offers to save the printable
  /// sheet (QR + code + instructions).
  Future<void> _showRecoverySheet(BuildContext context) async {
    final t = context.t;
    final messenger = ScaffoldMessenger.of(context);
    final code = await _service.recoveryCode();
    if (!context.mounted) return;
    final save = await showDialog<bool>(
      context: context,
      builder: (dialogContext) => AlertDialog(
        title: Text(t.backup.recoveryTitle),
        content: Column(
          mainAxisSize: MainAxisSize.min,
          crossAxisAlignment: CrossAxisAlignment.start,
          children: [
            Text(t.backup.recoveryIntro),
            const SizedBox(height: 12),
            SelectableText(
              code,
              key: const Key('backup.recoveryCode'),
              style: const TextStyle(fontWeight: FontWeight.w600),
            ),
          ],
        ),
        actions: [
          TextButton(
            key: const Key('backup.recoveryCopy'),
            onPressed: () async {
              await Clipboard.setData(ClipboardData(text: code));
              if (dialogContext.mounted) Navigator.pop(dialogContext, false);
            },
            child: Text(t.backup.recoveryCopy),
          ),
          FilledButton(
            key: const Key('backup.recoverySave'),
            onPressed: () => Navigator.pop(dialogContext, true),
            child: Text(t.backup.recoverySave),
          ),
        ],
      ),
    );
    if (save != true) return;
    try {
      final saved = await _sheet.saveSheet(
        title: t.backup.recoverySheetTitle,
        intro: t.backup.recoveryIntro,
        code: code,
        suggestedName: 'tanemaki-recovery.pdf',
      );
      _show(messenger, saved ? t.backup.exportSaved : t.backup.cancelled);
    } on Object {
      _show(messenger, t.backup.failed);
    }
  }

  Future<void> _runRestore(BuildContext context) async {
    final t = context.t;
    final messenger = ScaffoldMessenger.of(context);
    final confirmed = await _confirm(
      context,
      title: t.backup.importConfirmTitle,
      body: t.backup.importConfirmBody,
    );
    if (confirmed != true || !context.mounted) return;

    final PendingBackup? pending;
    try {
      pending = await _service.pickBackup();
    } on Object {
      _show(messenger, t.backup.failed);
      return;
    }
    if (pending == null) {
      _show(messenger, t.backup.cancelled);
      return;
    }

    try {
      _report(messenger, t, await _service.restoreBackup(pending));
      return;
    } on BackupAuthException {
      // Sealed under another identity: ask for the printed code below.
    } on FormatException {
      _show(messenger, t.backup.importFailed);
      return;
    } on Object {
      _show(messenger, t.backup.failed);
      return;
    }

    if (!context.mounted) return;
    final code = await _askRecoveryCode(context);
    if (code == null) {
      _show(messenger, t.backup.cancelled);
      return;
    }
    try {
      _report(
        messenger,
        t,
        await _service.restoreBackup(pending, recoveryCode: code),
      );
    } on BackupAuthException {
      _show(messenger, t.backup.recoveryWrongCode);
    } on FormatException {
      _show(messenger, t.backup.recoveryWrongCode);
    } on Object {
      _show(messenger, t.backup.failed);
    }
  }

  /// Asks for the code printed on the recovery sheet.
  Future<String?> _askRecoveryCode(BuildContext context) {
    final t = context.t;
    final controller = TextEditingController();
    return showDialog<String>(
      context: context,
      builder: (dialogContext) => AlertDialog(
        title: Text(t.backup.recoveryPromptTitle),
        content: Column(
          mainAxisSize: MainAxisSize.min,
          crossAxisAlignment: CrossAxisAlignment.start,
          children: [
            Text(t.backup.recoveryPromptBody),
            const SizedBox(height: 12),
            TextField(
              key: const Key('backup.recoveryField'),
              controller: controller,
              autofocus: true,
              textCapitalization: TextCapitalization.characters,
              decoration: InputDecoration(
                labelText: t.backup.recoveryTitle,
                border: const OutlineInputBorder(
                  borderRadius: BorderRadius.all(Radius.circular(8)),
                ),
              ),
            ),
          ],
        ),
        actions: [
          TextButton(
            onPressed: () => Navigator.pop(dialogContext),
            child: Text(t.common.cancel),
          ),
          FilledButton(
            key: const Key('backup.recoveryConfirm'),
            onPressed: () =>
                Navigator.pop(dialogContext, controller.text.trim()),
            child: Text(t.backup.importAction),
          ),
        ],
      ),
    );
  }

  Future<void> _runImportCsv(BuildContext context) async {
    final t = context.t;
    final messenger = ScaffoldMessenger.of(context);
    final confirmed = await _confirm(
      context,
      title: t.backup.importCsvConfirmTitle,
      body: t.backup.importCsvConfirmBody,
    );
    if (confirmed != true) return;
    try {
      final summary = await _service.importCsv();
      _show(
        messenger,
        summary == null
            ? t.backup.cancelled
            : t.backup.importCsvDone(count: summary.inserted),
      );
    } on FormatException {
      _show(messenger, t.backup.importFailed);
    } on Object {
      _show(messenger, t.backup.failed);
    }
  }

  Future<bool?> _confirm(
    BuildContext context, {
    required String title,
    required String body,
  }) {
    final t = context.t;
    return showDialog<bool>(
      context: context,
      builder: (dialogContext) => AlertDialog(
        title: Text(title),
        content: Text(body),
        actions: [
          TextButton(
            onPressed: () => Navigator.of(dialogContext).pop(false),
            child: Text(t.common.cancel),
          ),
          FilledButton(
            onPressed: () => Navigator.of(dialogContext).pop(true),
            child: Text(t.backup.importAction),
          ),
        ],
      ),
    );
  }

  void _report(
    ScaffoldMessengerState messenger,
    Translations t,
    ImportSummary summary,
  ) {
    _show(
      messenger,
      t.backup.importDone(added: summary.inserted, updated: summary.updated),
    );
  }

  void _show(ScaffoldMessengerState messenger, String message) {
    messenger.showSnackBar(SnackBar(content: Text(message)));
  }
}
