import 'package:flutter/material.dart'; import 'package:lucide_icons/lucide_icons.dart'; import '../../../app/theme/app_colors.dart'; import '../../../data/services/muslim_api_service.dart'; class QuranEnrichmentScreen extends StatefulWidget { const QuranEnrichmentScreen({super.key}); @override State createState() => _QuranEnrichmentScreenState(); } class _QuranEnrichmentScreenState extends State with SingleTickerProviderStateMixin { late TabController _tabController; final TextEditingController _searchController = TextEditingController(); final TextEditingController _pageController = TextEditingController(text: '1'); List> _surahs = []; List> _searchResults = []; List> _tafsirItems = []; List> _asbabItems = []; List> _juzItems = []; List> _pageItems = []; List> _themeItems = []; List> _asmaItems = []; int _selectedSurahId = 1; int _selectedPage = 1; bool _loadingInit = true; bool _loadingSearch = false; bool _loadingTafsir = false; bool _loadingAsbab = false; bool _loadingPage = false; String? _error; final Set _expandedWordByWord = {}; final Map>> _wordByWord = {}; final Set _loadingWordByWord = {}; @override void initState() { super.initState(); _tabController = TabController(length: 7, vsync: this); _bootstrap(); } @override void dispose() { _tabController.dispose(); _searchController.dispose(); _pageController.dispose(); super.dispose(); } Future _bootstrap() async { setState(() { _loadingInit = true; _error = null; }); try { final surahs = await MuslimApiService.instance.getAllSurahs(); final juz = await MuslimApiService.instance.getJuzList(); final themes = await MuslimApiService.instance.getThemes(); final asma = await MuslimApiService.instance.getAsmaulHusna(); if (!mounted) return; setState(() { _surahs = surahs; _selectedSurahId = surahs.isNotEmpty ? ((surahs.first['nomor'] as int?) ?? 1) : 1; _juzItems = juz; _themeItems = themes; _asmaItems = asma; _loadingInit = false; }); await _loadTafsirForSelectedSurah(); await _loadAsbabForSelectedSurah(); await _loadPageAyah(); } catch (_) { if (!mounted) return; setState(() { _loadingInit = false; _error = 'Gagal memuat data enrichment'; }); } } Future _runSearch() async { final query = _searchController.text.trim(); if (query.isEmpty) { setState(() => _searchResults = []); return; } setState(() => _loadingSearch = true); final result = await MuslimApiService.instance.searchAyah(query); if (!mounted) return; setState(() { _searchResults = result; _loadingSearch = false; }); } Future _loadTafsirForSelectedSurah() async { setState(() => _loadingTafsir = true); final result = await MuslimApiService.instance.getTafsirBySurah(_selectedSurahId); if (!mounted) return; setState(() { _tafsirItems = result; _loadingTafsir = false; }); } Future _loadAsbabForSelectedSurah() async { setState(() => _loadingAsbab = true); final result = await MuslimApiService.instance.getAsbabBySurah(_selectedSurahId); if (!mounted) return; setState(() { _asbabItems = result; _loadingAsbab = false; }); } Future _loadPageAyah() async { setState(() => _loadingPage = true); final page = int.tryParse(_pageController.text.trim()) ?? _selectedPage; final safePage = page.clamp(1, 604); final result = await MuslimApiService.instance.getAyahByPage(safePage); if (!mounted) return; setState(() { _selectedPage = safePage; _pageController.text = '$safePage'; _pageItems = result; _loadingPage = false; }); } Future _toggleWordByWord(Map ayah) async { final surah = (ayah['surah'] as num?)?.toInt(); final ayahNum = (ayah['ayah'] as num?)?.toInt(); if (surah == null || ayahNum == null) return; final key = '$surah:$ayahNum'; final expanded = _expandedWordByWord.contains(key); if (expanded) { setState(() => _expandedWordByWord.remove(key)); return; } if (_wordByWord.containsKey(key)) { setState(() => _expandedWordByWord.add(key)); return; } setState(() { _loadingWordByWord.add(key); _expandedWordByWord.add(key); }); final words = await MuslimApiService.instance.getWordByWord(surah, ayahNum); if (!mounted) return; setState(() { _wordByWord[key] = words; _loadingWordByWord.remove(key); }); } String _surahNameById(int surahId) { for (final s in _surahs) { if (s['nomor'] == surahId) { return s['namaLatin']?.toString() ?? 'Surah $surahId'; } } return 'Surah $surahId'; } @override Widget build(BuildContext context) { final isDark = Theme.of(context).brightness == Brightness.dark; return Scaffold( appBar: AppBar( title: const Text('Quran Enrichment'), actions: [ IconButton( onPressed: _bootstrap, icon: const Icon(LucideIcons.refreshCw), tooltip: 'Muat ulang', ), ], bottom: TabBar( controller: _tabController, isScrollable: true, labelColor: AppColors.primary, unselectedLabelColor: isDark ? AppColors.textSecondaryDark : AppColors.textSecondaryLight, indicatorColor: AppColors.primary, tabs: const [ Tab(text: 'Cari'), Tab(text: 'Tafsir'), Tab(text: 'Asbab'), Tab(text: 'Juz'), Tab(text: 'Halaman'), Tab(text: 'Tema'), Tab(text: 'Asmaul Husna'), ], ), ), body: _loadingInit ? const Center(child: CircularProgressIndicator()) : _error != null ? Center( child: Text( _error!, style: TextStyle( color: isDark ? AppColors.textSecondaryDark : AppColors.textSecondaryLight, ), ), ) : TabBarView( controller: _tabController, children: [ _buildSearchTab(context, isDark), _buildTafsirTab(context, isDark), _buildAsbabTab(context, isDark), _buildJuzTab(context, isDark), _buildPageTab(context, isDark), _buildThemeTab(context, isDark), _buildAsmaTab(context, isDark), ], ), ); } Widget _buildSearchTab(BuildContext context, bool isDark) { return Column( children: [ Padding( padding: const EdgeInsets.fromLTRB(16, 12, 16, 8), child: Row( children: [ Expanded( child: TextField( controller: _searchController, textInputAction: TextInputAction.search, onSubmitted: (_) => _runSearch(), decoration: InputDecoration( hintText: 'Cari ayat, tema, atau kata kunci...', prefixIcon: const Icon(LucideIcons.search), border: OutlineInputBorder( borderRadius: BorderRadius.circular(12), ), ), ), ), const SizedBox(width: 8), FilledButton( onPressed: _runSearch, child: const Text('Cari'), ), ], ), ), Expanded( child: _loadingSearch ? const Center(child: CircularProgressIndicator()) : _searchResults.isEmpty ? Center( child: Text( 'Belum ada hasil pencarian', style: TextStyle( color: isDark ? AppColors.textSecondaryDark : AppColors.textSecondaryLight, ), ), ) : ListView.builder( padding: const EdgeInsets.fromLTRB(16, 8, 16, 16), itemCount: _searchResults.length, itemBuilder: (context, index) { final ayah = _searchResults[index]; final surahId = (ayah['surah'] as num?)?.toInt() ?? 0; final ayahNum = (ayah['ayah'] as num?)?.toInt() ?? 0; final key = '$surahId:$ayahNum'; final expanded = _expandedWordByWord.contains(key); final words = _wordByWord[key] ?? const []; final loadingWords = _loadingWordByWord.contains(key); return Container( margin: const EdgeInsets.only(bottom: 12), padding: const EdgeInsets.all(14), decoration: BoxDecoration( color: isDark ? AppColors.surfaceDark : AppColors.surfaceLight, borderRadius: BorderRadius.circular(14), border: Border.all( color: isDark ? AppColors.primary.withValues(alpha: 0.1) : AppColors.cream, ), ), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Text( '${_surahNameById(surahId)} : $ayahNum', style: const TextStyle( fontWeight: FontWeight.w700, color: AppColors.primary, ), ), TextButton.icon( onPressed: () => _toggleWordByWord(ayah), icon: Icon( expanded ? LucideIcons.chevronUp : LucideIcons.languages, size: 16, ), label: Text( expanded ? 'Tutup' : 'Per Kata', ), ), ], ), const SizedBox(height: 8), Align( alignment: Alignment.centerRight, child: Text( ayah['arab']?.toString() ?? '', textAlign: TextAlign.right, style: const TextStyle( fontFamily: 'Amiri', fontSize: 24, fontWeight: FontWeight.w400, height: 1.8, ), ), ), const SizedBox(height: 8), Text( ayah['text']?.toString() ?? '', style: TextStyle( color: isDark ? AppColors.textSecondaryDark : AppColors.textSecondaryLight, ), ), if (expanded) ...[ const SizedBox(height: 12), if (loadingWords) const Padding( padding: EdgeInsets.symmetric(vertical: 8), child: Center( child: CircularProgressIndicator(), ), ) else if (words.isEmpty) Text( 'Data kata tidak tersedia untuk ayat ini.', style: TextStyle( color: isDark ? AppColors.textSecondaryDark : AppColors.textSecondaryLight, ), ) else Wrap( spacing: 8, runSpacing: 8, children: words.map((word) { return Container( padding: const EdgeInsets.all(10), decoration: BoxDecoration( color: AppColors.primary .withValues(alpha: 0.08), borderRadius: BorderRadius.circular(10), ), child: Column( mainAxisSize: MainAxisSize.min, children: [ Text( word['arab']?.toString() ?? '', style: const TextStyle( fontFamily: 'Amiri', fontSize: 18, fontWeight: FontWeight.w400, ), ), const SizedBox(height: 2), Text( word['word']?.toString() ?? '', style: const TextStyle( fontWeight: FontWeight.w700, fontSize: 12, ), ), const SizedBox(height: 2), Text( word['indo']?.toString() ?? '', style: TextStyle( fontSize: 11, color: isDark ? AppColors .textSecondaryDark : AppColors .textSecondaryLight, ), ), ], ), ); }).toList(), ), ], ], ), ); }, ), ), ], ); } Widget _buildTafsirTab(BuildContext context, bool isDark) { return Column( children: [ _buildSurahSelector( onChanged: (value) { setState(() => _selectedSurahId = value); _loadTafsirForSelectedSurah(); }, ), Expanded( child: _loadingTafsir ? const Center(child: CircularProgressIndicator()) : _tafsirItems.isEmpty ? _emptyText(isDark, 'Belum ada data tafsir untuk surah ini') : ListView.builder( padding: const EdgeInsets.fromLTRB(16, 8, 16, 16), itemCount: _tafsirItems.length, itemBuilder: (context, index) { final item = _tafsirItems[index]; final ayah = item['nomorAyat']?.toString() ?? '-'; final wajiz = item['wajiz']?.toString() ?? ''; final tahlili = item['tahlili']?.toString() ?? ''; return _buildCard( isDark, title: 'Ayat $ayah', body: '$wajiz\n\n$tahlili', ); }, ), ), ], ); } Widget _buildAsbabTab(BuildContext context, bool isDark) { return Column( children: [ _buildSurahSelector( onChanged: (value) { setState(() => _selectedSurahId = value); _loadAsbabForSelectedSurah(); }, ), Expanded( child: _loadingAsbab ? const Center(child: CircularProgressIndicator()) : _asbabItems.isEmpty ? _emptyText( isDark, 'Belum ada data asbabun nuzul untuk surah ini', ) : ListView.builder( padding: const EdgeInsets.fromLTRB(16, 8, 16, 16), itemCount: _asbabItems.length, itemBuilder: (context, index) { final item = _asbabItems[index]; final ayah = item['nomorAyat']?.toString() ?? '-'; return _buildCard( isDark, title: 'Ayat $ayah', body: item['text']?.toString() ?? '', ); }, ), ), ], ); } Widget _buildJuzTab(BuildContext context, bool isDark) { if (_juzItems.isEmpty) { return _emptyText(isDark, 'Data juz tidak tersedia'); } return ListView.builder( padding: const EdgeInsets.fromLTRB(16, 12, 16, 16), itemCount: _juzItems.length, itemBuilder: (context, index) { final item = _juzItems[index]; final number = item['number']?.toString() ?? '-'; final startName = item['name_start_id']?.toString() ?? '-'; final endName = item['name_end_id']?.toString() ?? '-'; final startVerse = item['verse_start']?.toString() ?? '-'; final endVerse = item['verse_end']?.toString() ?? '-'; return _buildCard( isDark, title: 'Juz $number', body: 'Mulai: $startName ayat $startVerse\nSelesai: $endName ayat $endVerse', ); }, ); } Widget _buildPageTab(BuildContext context, bool isDark) { return Column( children: [ Padding( padding: const EdgeInsets.fromLTRB(16, 12, 16, 8), child: Row( children: [ Expanded( child: TextField( controller: _pageController, keyboardType: TextInputType.number, decoration: InputDecoration( labelText: 'Nomor Halaman (1-604)', border: OutlineInputBorder( borderRadius: BorderRadius.circular(12), ), ), ), ), const SizedBox(width: 8), FilledButton( onPressed: _loadPageAyah, child: const Text('Tampilkan'), ), ], ), ), Expanded( child: _loadingPage ? const Center(child: CircularProgressIndicator()) : _pageItems.isEmpty ? _emptyText(isDark, 'Tidak ada data untuk halaman ini') : ListView.builder( padding: const EdgeInsets.fromLTRB(16, 8, 16, 16), itemCount: _pageItems.length, itemBuilder: (context, index) { final item = _pageItems[index]; final surahId = (item['surah'] as num?)?.toInt() ?? 0; final ayah = item['ayah']?.toString() ?? '-'; return _buildCard( isDark, title: '${_surahNameById(surahId)} : $ayah', body: '${item['arab']?.toString() ?? ''}\n\n${item['text']?.toString() ?? ''}', ); }, ), ), ], ); } Widget _buildThemeTab(BuildContext context, bool isDark) { if (_themeItems.isEmpty) { return _emptyText(isDark, 'Data tema belum tersedia'); } return ListView.builder( padding: const EdgeInsets.fromLTRB(16, 12, 16, 16), itemCount: _themeItems.length, itemBuilder: (context, index) { final item = _themeItems[index]; return _buildCard( isDark, title: 'Tema #${item['id'] ?? '-'}', body: item['name']?.toString() ?? '', ); }, ); } Widget _buildAsmaTab(BuildContext context, bool isDark) { if (_asmaItems.isEmpty) { return _emptyText(isDark, 'Data Asmaul Husna tidak tersedia'); } return ListView.builder( padding: const EdgeInsets.fromLTRB(16, 12, 16, 16), itemCount: _asmaItems.length, itemBuilder: (context, index) { final item = _asmaItems[index]; return Container( margin: const EdgeInsets.only(bottom: 10), padding: const EdgeInsets.all(14), decoration: BoxDecoration( color: isDark ? AppColors.surfaceDark : AppColors.surfaceLight, borderRadius: BorderRadius.circular(12), border: Border.all( color: isDark ? AppColors.primary.withValues(alpha: 0.1) : AppColors.cream, ), ), child: Row( children: [ Container( width: 40, height: 40, alignment: Alignment.center, decoration: BoxDecoration( color: AppColors.primary.withValues(alpha: 0.1), borderRadius: BorderRadius.circular(10), ), child: Text( '${item['id'] ?? '-'}', style: const TextStyle( fontWeight: FontWeight.w700, color: AppColors.primary, ), ), ), const SizedBox(width: 12), Expanded( child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( item['arab']?.toString() ?? '', style: const TextStyle( fontFamily: 'Amiri', fontSize: 22, fontWeight: FontWeight.w400, ), ), Text( item['latin']?.toString() ?? '', style: const TextStyle( fontWeight: FontWeight.w700, color: AppColors.primary, ), ), const SizedBox(height: 2), Text( item['indo']?.toString() ?? '', style: TextStyle( color: isDark ? AppColors.textSecondaryDark : AppColors.textSecondaryLight, ), ), ], ), ), ], ), ); }, ); } Widget _buildSurahSelector({required ValueChanged onChanged}) { if (_surahs.isEmpty) { return Padding( padding: const EdgeInsets.fromLTRB(16, 12, 16, 8), child: _emptyText( Theme.of(context).brightness == Brightness.dark, 'Data surah tidak tersedia', ), ); } return Padding( padding: const EdgeInsets.fromLTRB(16, 12, 16, 8), child: Container( padding: const EdgeInsets.symmetric(horizontal: 12), decoration: BoxDecoration( border: Border.all(color: AppColors.primary.withValues(alpha: 0.2)), borderRadius: BorderRadius.circular(12), ), child: DropdownButtonHideUnderline( child: DropdownButton( value: _selectedSurahId, isExpanded: true, items: _surahs.map((surah) { final id = (surah['nomor'] as num?)?.toInt() ?? 1; final name = surah['namaLatin']?.toString() ?? 'Surah $id'; return DropdownMenuItem( value: id, child: Text('$id. $name'), ); }).toList(), onChanged: (value) { if (value == null) return; onChanged(value); }, ), ), ), ); } Widget _buildCard(bool isDark, {required String title, required String body}) { return Container( margin: const EdgeInsets.only(bottom: 10), padding: const EdgeInsets.all(14), decoration: BoxDecoration( color: isDark ? AppColors.surfaceDark : AppColors.surfaceLight, borderRadius: BorderRadius.circular(12), border: Border.all( color: isDark ? AppColors.primary.withValues(alpha: 0.1) : AppColors.cream, ), ), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( title, style: const TextStyle( fontWeight: FontWeight.w700, color: AppColors.primary, ), ), const SizedBox(height: 8), Text(body, style: const TextStyle(height: 1.5)), ], ), ); } Widget _emptyText(bool isDark, String text) { return Center( child: Text( text, textAlign: TextAlign.center, style: TextStyle( color: isDark ? AppColors.textSecondaryDark : AppColors.textSecondaryLight, ), ), ); } }