import 'package:flutter/material.dart'; import 'package:lucide_icons/lucide_icons.dart'; import '../../services/backup_service.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 { setState(() => _state = _BackupState.signingIn); final account = await _backupService.signIn(); if (mounted) { setState(() => _state = _BackupState.idle); if (account != null) { ScaffoldMessenger.of(context).showSnackBar( SnackBar(content: Text('${account.email} で連携しました')), ); } else { ScaffoldMessenger.of(context).showSnackBar( const SnackBar(content: Text('連携がキャンセルされました')), ); } } } Future _signOut() async { final confirmed = await showDialog( context: context, builder: (context) => AlertDialog( title: const Text('連携解除'), content: const Text('Googleアカウントとの連携を解除しますか?'), 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); ScaffoldMessenger.of(context).showSnackBar( const SnackBar(content: Text('連携を解除しました')), ); } } } Future _createBackup() async { setState(() => _state = _BackupState.backingUp); final success = await _backupService.createBackup(); if (mounted) { setState(() => _state = _BackupState.idle); ScaffoldMessenger.of(context).showSnackBar( SnackBar( content: Text(success ? 'バックアップが完了しました' : 'バックアップに失敗しました'), backgroundColor: success ? Colors.green : Colors.red, ), ); } } Future _restoreBackup() async { final hasBackup = await _backupService.hasBackupOnDrive(); if (!hasBackup && mounted) { ScaffoldMessenger.of(context).showSnackBar( const SnackBar(content: Text('バックアップファイルが見つかりません')), ); return; } final confirmed = await showDialog( context: context, builder: (context) => AlertDialog( title: Row( children: [ Icon(LucideIcons.alertTriangle, color: Colors.orange, 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: Theme.of(context).primaryColor, foregroundColor: Colors.white, ), child: const Text('復元'), ), ], ), ); if (confirmed == true) { setState(() => _state = _BackupState.restoring); final success = await _backupService.restoreBackup(); if (mounted) { setState(() => _state = _BackupState.idle); ScaffoldMessenger.of(context).showSnackBar( SnackBar( content: Text(success ? '復元が完了しました' : '復元に失敗しました'), backgroundColor: success ? Colors.green : Colors.red, ), ); } } } @override Widget build(BuildContext context) { final currentUser = _backupService.currentUser; final isDark = Theme.of(context).brightness == Brightness.dark; final isAnyProcessing = _state != _BackupState.idle; return Column( children: [ _buildSectionHeader(context, widget.title, LucideIcons.cloud), // Wi-Fi推奨の注意書き (v1.2) Container( margin: const EdgeInsets.only(bottom: 12), padding: const EdgeInsets.all(12), decoration: BoxDecoration( color: Colors.blue.withValues(alpha: 0.1), borderRadius: BorderRadius.circular(8), border: Border.all(color: Colors.blue.withValues(alpha: 0.3)), ), child: Row( crossAxisAlignment: CrossAxisAlignment.start, children: [ Icon(LucideIcons.wifi, color: Colors.blue, size: 20), const SizedBox(width: 12), Expanded( child: Text( 'バックアップにはWi-Fi環境を推奨します\n画像が多い場合、数百MB〜1GB以上になる可能性があります', style: TextStyle( fontSize: 12, color: isDark ? Colors.blue[300] : Colors.blue[900], ), ), ), ], ), ), Card( color: isDark ? const Color(0xFF1E1E1E) : null, child: Column( children: [ // Google Sign In Status ListTile( leading: Icon( currentUser != null ? LucideIcons.checkCircle2 : LucideIcons.user, color: currentUser != null ? Colors.green : (isDark ? Colors.orange[300] : Theme.of(context).primaryColor), ), title: Text(currentUser == null ? 'Googleアカウント連携' : currentUser.email), subtitle: Text(currentUser == 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 ? Theme.of(context).primaryColor : Colors.grey[700], foregroundColor: Colors.white, padding: const EdgeInsets.symmetric(horizontal: 16), ), onPressed: isAnyProcessing ? null : (currentUser == null ? _signIn : _signOut), child: Text(currentUser == null ? '連携' : '解除'), ), ), if (currentUser != null) ...[ const Divider(height: 1), ListTile( leading: Icon(LucideIcons.uploadCloud, color: isDark ? Colors.blue[300] : Colors.blue), title: const Text('バックアップ'), subtitle: const Text('Google Driveに最新データのみ保存(過去分は上書き)'), trailing: _state == _BackupState.backingUp ? const SizedBox(width: 24, height: 24, child: CircularProgressIndicator(strokeWidth: 2)) : null, onTap: isAnyProcessing ? null : _createBackup, ), const Divider(height: 1), ListTile( leading: Icon(LucideIcons.downloadCloud, color: isDark ? Colors.red[300] : Colors.red), title: const Text('データ復元'), subtitle: const Text('Google Driveからデータを読み込み(上書き)'), 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 isDark = Theme.of(context).brightness == Brightness.dark; return Padding( padding: const EdgeInsets.symmetric(vertical: 8, horizontal: 4), child: Row( children: [ Icon(icon, size: 20, color: isDark ? Colors.orange[300] : Theme.of(context).primaryColor), const SizedBox(width: 8), Text( title, style: Theme.of(context).textTheme.titleMedium?.copyWith( fontWeight: FontWeight.bold, color: isDark ? Colors.grey[300] : Theme.of(context).primaryColor, ), ), ], ), ); } }