import 'package:flutter/material.dart'; import 'package:lucide_icons/lucide_icons.dart'; import '../../services/backup_service.dart'; import '../../theme/app_colors.dart'; class BackupSettingsSection extends StatefulWidget { final String title; const BackupSettingsSection({ super.key, this.title = 'バックアップ・復元', }); @override State createState() => _BackupSettingsSectionState(); } enum _BackupState { idle, signingIn, signingOut, backingUp, restoring } class _BackupSettingsSectionState extends State { final BackupService _backupService = BackupService(); _BackupState _state = _BackupState.idle; @override void initState() { super.initState(); _initBackupService(); } Future _initBackupService() async { await _backupService.init(); if (mounted) { setState(() {}); } } Future _signIn() async { final messenger = ScaffoldMessenger.of(context); setState(() => _state = _BackupState.signingIn); final account = await _backupService.signIn(); if (mounted) { setState(() => _state = _BackupState.idle); if (account != null) { messenger.showSnackBar( SnackBar(content: Text('${account.email} で連携しました')), ); } else { messenger.showSnackBar( const SnackBar(content: Text('連携がキャンセルされました')), ); } } } Future _signOut() async { final messenger = ScaffoldMessenger.of(context); final confirmed = await showDialog( context: context, builder: (context) => AlertDialog( title: const Text('連携解除'), content: const Text('Googleアカウントとの連携を解除しますか?\n安全にログアウトできます。'), actions: [ TextButton( onPressed: () => Navigator.pop(context, false), child: const Text('キャンセル'), ), ElevatedButton( onPressed: () => Navigator.pop(context, true), child: const Text('解除'), ), ], ), ); if (confirmed == true) { setState(() => _state = _BackupState.signingOut); await _backupService.signOut(); if (mounted) { setState(() => _state = _BackupState.idle); messenger.showSnackBar( const SnackBar(content: Text('連携を解除しました')), ); } } } Future _createBackup() async { final messenger = ScaffoldMessenger.of(context); setState(() => _state = _BackupState.backingUp); final success = await _backupService.createBackup(); if (mounted) { setState(() => _state = _BackupState.idle); messenger.showSnackBar( SnackBar( content: Text(success ? 'バックアップが完了しました' : 'バックアップに失敗しました'), // Snackbars can keep Green/Red for semantic clarity, or be neutral. // User asked to remove Green/Red icons from the UI, but feedback (Snackbar) usually stays semantic. // However, to be safe and "Washi", let's use Sumi (Black) for success? // Or just leave snackbars as they are ephemeral. The request was likely about the visible static UI. backgroundColor: success ? Colors.green : Colors.red, ), ); } } Future _confirmBackup() async { final appColors = Theme.of(context).extension()!; final confirmed = await showDialog( context: context, builder: (context) => AlertDialog( title: const Text('バックアップ'), content: const Text('Google Driveに最新データのみ保存(過去分は上書き)されます。\n本当に続行しますか?'), actions: [ TextButton( onPressed: () => Navigator.pop(context, false), child: const Text('キャンセル'), ), ElevatedButton( onPressed: () => Navigator.pop(context, true), style: ElevatedButton.styleFrom( backgroundColor: appColors.brandPrimary, foregroundColor: appColors.surfaceSubtle, ), child: const Text('バックアップ'), ), ], ), ); if (confirmed == true && mounted) { await _createBackup(); } } Future _restoreBackup() async { final messenger = ScaffoldMessenger.of(context); final appColors = Theme.of(context).extension()!; // Note: hasBackup check is async final hasBackup = await _backupService.hasBackupOnDrive(); if (!hasBackup) { if (mounted) { messenger.showSnackBar( const SnackBar(content: Text('バックアップファイルが見つかりません')), ); } return; } if (!mounted) return; final confirmed = await showDialog( context: context, builder: (context) => AlertDialog( title: Row( children: [ Icon(LucideIcons.alertTriangle, color: appColors.warning, size: 24), const SizedBox(width: 8), const Text('データ復元'), ], ), content: const Text('現在のデータは上書きされます。\n削除されたデータは元に戻りません。\n\n本当に続行しますか?'), actions: [ TextButton( onPressed: () => Navigator.pop(context, false), child: const Text('キャンセル'), ), ElevatedButton( onPressed: () => Navigator.pop(context, true), style: ElevatedButton.styleFrom( backgroundColor: appColors.warning, foregroundColor: appColors.surfaceSubtle, ), child: const Text('復元'), ), ], ), ); if (confirmed == true && mounted) { setState(() => _state = _BackupState.restoring); final success = await _backupService.restoreBackup(); if (mounted) { setState(() => _state = _BackupState.idle); messenger.showSnackBar( SnackBar( content: Text(success ? '復元が完了しました' : '復元に失敗しました'), backgroundColor: success ? Colors.green : Colors.red, ), ); } } } @override Widget build(BuildContext context) { final currentUser = _backupService.currentUser; final isAnyProcessing = _state != _BackupState.idle; final appColors = Theme.of(context).extension()!; return Column( children: [ _buildSectionHeader(context, widget.title, LucideIcons.cloud), // Wi-Fi warning Container( margin: const EdgeInsets.only(bottom: 12), padding: const EdgeInsets.all(12), decoration: BoxDecoration( color: appColors.info.withValues(alpha: 0.1), borderRadius: BorderRadius.circular(8), border: Border.all(color: appColors.info.withValues(alpha: 0.3)), ), child: Row( crossAxisAlignment: CrossAxisAlignment.start, children: [ Icon(LucideIcons.wifi, color: appColors.info, size: 20), const SizedBox(width: 12), Expanded( child: Text( 'Wi-Fi環境下での実行を推奨します\nデータ量が100MB以上になる可能性があります', style: TextStyle( fontSize: 12, color: appColors.textSecondary, fontWeight: FontWeight.w500, ), ), ), ], ), ), Card( color: appColors.surfaceSubtle, elevation: 0, margin: EdgeInsets.zero, child: Column( children: [ ListTile( leading: Icon( currentUser != null ? LucideIcons.checkCircle2 : LucideIcons.user, color: currentUser != null ? appColors.success : appColors.iconSubtle, ), title: Text(currentUser == null ? 'Googleアカウント連携' : currentUser.email, style: TextStyle(color: appColors.textPrimary)), subtitle: currentUser == null ? Text('Google Driveにバックアップ', style: TextStyle(color: appColors.textSecondary)) : null, trailing: (_state == _BackupState.signingIn || _state == _BackupState.signingOut) ? const SizedBox(width: 24, height: 24, child: CircularProgressIndicator(strokeWidth: 2)) : ElevatedButton( style: ElevatedButton.styleFrom( backgroundColor: currentUser == null ? appColors.brandPrimary : appColors.error, foregroundColor: appColors.surfaceSubtle, padding: const EdgeInsets.symmetric(horizontal: 16), ), onPressed: isAnyProcessing ? null : (currentUser == null ? _signIn : _signOut), child: Text( currentUser == null ? '連携' : '解除', style: const TextStyle(fontWeight: FontWeight.bold), ), ), ), if (currentUser != null) ...[ Divider(height: 1, color: appColors.divider), ListTile( leading: Icon(LucideIcons.uploadCloud, color: appColors.iconDefault), title: Text('バックアップ', style: TextStyle(color: appColors.textPrimary)), trailing: _state == _BackupState.backingUp ? const SizedBox(width: 24, height: 24, child: CircularProgressIndicator(strokeWidth: 2)) : null, onTap: isAnyProcessing ? null : _confirmBackup, ), Divider(height: 1, color: appColors.divider), ListTile( leading: Icon(LucideIcons.downloadCloud, color: appColors.iconDefault), title: Text('データ復元', style: TextStyle(color: appColors.textPrimary)), trailing: _state == _BackupState.restoring ? const SizedBox(width: 24, height: 24, child: CircularProgressIndicator(strokeWidth: 2)) : null, onTap: isAnyProcessing ? null : _restoreBackup, ), ], ], ), ), ], ); } Widget _buildSectionHeader(BuildContext context, String title, IconData icon) { final appColors = Theme.of(context).extension()!; return Padding( padding: const EdgeInsets.symmetric(vertical: 8, horizontal: 4), child: Row( children: [ Icon(icon, size: 20, color: appColors.iconDefault), const SizedBox(width: 8), Text( title, style: Theme.of(context).textTheme.titleMedium?.copyWith( fontWeight: FontWeight.bold, color: appColors.textPrimary, ), ), ], ), ); } }