import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import '../../data/ai/model_lifecycle.dart'; import '../../state/ai_providers.dart'; class SettingsScreen extends ConsumerWidget { const SettingsScreen({super.key}); @override Widget build(BuildContext context, WidgetRef ref) { return Scaffold( appBar: AppBar(title: const Text('설정')), body: ListView( children: const [ _SectionHeader('AI 도움'), _AiSection(), ], ), ); } } class _SectionHeader extends StatelessWidget { final String title; const _SectionHeader(this.title); @override Widget build(BuildContext context) { return Padding( padding: const EdgeInsets.fromLTRB(16, 16, 16, 4), child: Text( title, style: Theme.of(context).textTheme.titleSmall, ), ); } } class _AiSection extends ConsumerWidget { const _AiSection(); @override Widget build(BuildContext context, WidgetRef ref) { final settings = ref.watch(aiSettingsProvider); final availability = ref.watch(modelAvailabilityProvider); return Column( crossAxisAlignment: CrossAxisAlignment.stretch, children: [ SwitchListTile( title: const Text('AI 도움 켜기'), subtitle: const Text( 'Gemma 4 E2B 모델 ≈ 1.5GB. 모든 처리는 단말에서 일어납니다.', ), value: settings.maybeWhen( data: (v) => v, orElse: () => false, ), onChanged: (v) async { if (v) { final ok = await _confirmOptIn(context); if (ok != true) return; } else { final ok = await _confirmOptOut(context); if (ok != true) return; } final freed = await ref.read(aiSettingsControllerProvider).setOptIn(v); if (!context.mounted) return; if (!v && freed > 0) { ScaffoldMessenger.of(context).showSnackBar( SnackBar(content: Text('공간 확보됨 ${_fmtMB(freed)}')), ); } }, ), availability.when( loading: () => const ListTile(title: Text('상태 확인 중...')), error: (e, _) => ListTile(title: Text('상태 오류: $e')), data: (a) => ListTile( title: const Text('모델 상태'), subtitle: Text(_describe(a)), ), ), const Padding( padding: EdgeInsets.fromLTRB(16, 8, 16, 16), child: Text( 'OQ-1 미해결: 정확한 모델 URL + SHA 가 픽스되기 전까지 ' '다운로드는 동작하지 않습니다. (Architect/Developer 인계 사항)', style: TextStyle(fontSize: 12, color: Colors.grey), ), ), ], ); } String _describe(ModelAvailability a) { switch (a) { case ModelAvailability.ready: return '사용 가능'; case ModelAvailability.missing: return '미설치 — 토글을 켜면 다운로드를 시작합니다'; case ModelAvailability.corrupt: return '손상됨 — 토글을 끄고 다시 켜주세요'; case ModelAvailability.downloading: return '다운로드 중 / 일시정지됨'; } } String _fmtMB(int bytes) { final mb = bytes / (1024 * 1024); return '${mb.toStringAsFixed(1)} MB'; } Future _confirmOptIn(BuildContext context) { return showDialog( context: context, builder: (_) => AlertDialog( title: const Text('AI 도움 켜기'), content: const Text( 'Gemma 4 E2B 모델 ≈ 1.5GB 를 다운로드합니다.\n' '- WiFi 권장\n' '- 모든 처리는 단말에서만 일어나며, 입력 텍스트는 외부로 전송되지 않습니다.\n' '- 끄면 즉시 삭제됩니다.', ), actions: [ TextButton( onPressed: () => Navigator.of(context).pop(false), child: const Text('취소'), ), FilledButton( onPressed: () => Navigator.of(context).pop(true), child: const Text('동의 후 다운로드'), ), ], ), ); } Future _confirmOptOut(BuildContext context) { return showDialog( context: context, builder: (_) => AlertDialog( title: const Text('AI 도움 끄기'), content: const Text( '모델 파일 ≈ 1.5GB 가 즉시 삭제됩니다. ' '다시 켜면 다시 다운로드해야 합니다.', ), actions: [ TextButton( onPressed: () => Navigator.of(context).pop(false), child: const Text('취소'), ), FilledButton( onPressed: () => Navigator.of(context).pop(true), child: const Text('끄고 삭제'), ), ], ), ); } }