import 'dart:convert'; import 'package:didvan/config/auth_config.dart'; import 'package:didvan/constants/assets.dart'; import 'package:didvan/models/category.dart'; import 'package:didvan/models/enums.dart'; import 'package:didvan/models/monthly/monthly_model.dart'; import 'package:didvan/providers/core.dart'; import 'package:didvan/services/content/content_service.dart'; import 'package:didvan/services/network/request.dart'; import 'package:didvan/services/network/request_helper.dart'; import 'package:flutter/material.dart'; import 'package:http/http.dart' as http; class MonthlyListState extends CoreProvier { List monthlyItems = []; List _allMonthlyItems = []; String searchQuery = ''; String? startDate; String? endDate; final List selectedCats = []; List categories = []; bool filtering = false; void initCategories() { categories = [ CategoryData( id: 1, label: 'اقتصادی', asset: Assets.economicCategoryIcon, ), CategoryData( id: 2, label: 'سیاسی', asset: Assets.politicalCategoryIcon, ), CategoryData( id: 3, label: 'فناوری', asset: Assets.techCategoryIcon, ), CategoryData( id: 4, label: 'کسب و کار', asset: Assets.businessCategoryIcon, ), CategoryData( id: 5, label: 'زیست‌محیطی', asset: Assets.enviromentalCategoryIcon, ), CategoryData( id: 6, label: 'اجتماعی', asset: Assets.socialCategoryIcon, ), ]; } bool get searching => searchQuery.isNotEmpty; /// شناسه‌ی دسته‌ی «ماهنامه» در content-service. static const String _monthlyCategoryId = 'd5e8b2c4-7f31-4a92-bc6f-8e2a5d1c3b47'; Future fetchMonthlyList() async { appState = AppState.busy; notifyListeners(); // مسیر جدید: content-service. if (!AuthConfig.useLegacyApi) { try { final res = await ContentService.instance.getContents( page: 1, limit: 30, filterStatus: '\$in:accepted,published', categoryId: _monthlyCategoryId, sortBy: const [MapEntry('createdAt', 'DESC')], ); if (!res.isSuccess || res.data == null) { appState = AppState.failed; notifyListeners(); return; } // endpoint لیست body نمی‌فرستد، پس برای هر آیتم detail کامل را // می‌گیریم تا fileUrl پر شود. final fetches = res.data!.data.map((c) async { final detail = await ContentService.instance.getContent(c.id); if (detail.isSuccess && detail.data != null) { return detail.data!; } return c; }).toList(); final fullItems = await Future.wait(fetches); _allMonthlyItems = fullItems.map((c) { final fileUrl = c.videoUrl ?? c.audioUrl ?? ''; final dateIso = (c.publishedAt ?? c.createdAt)?.toIso8601String() ?? DateTime.now().toIso8601String(); return MonthlyItem( id: c.id.hashCode, title: c.title, image: c.coverImage ?? '', description: c.summary ?? '', pageNumber: 0, publishedAt: dateIso, file: fileUrl, link: null, tags: const [], isLiked: c.isLiked, isBookmarked: c.isBookmarked, ); }).toList(); monthlyItems = _applyFilters(_allMonthlyItems); appState = AppState.idle; } catch (e) { debugPrint('❌ monthly (content-service) failed: $e'); appState = AppState.failed; } notifyListeners(); return; } // مسیر legacy. try { const url = '${RequestHelper.baseUrl}/monthly'; debugPrint('Fetching monthly list from: $url'); final response = await http.get( Uri.parse(url), headers: { 'Authorization': 'Bearer ${RequestService.token}', 'accept': '*/*', 'Content-Type': 'application/json; charset=UTF-8', }, ).timeout(const Duration(seconds: 30)); debugPrint('Response status code: ${response.statusCode}'); if (response.statusCode == 200) { final jsonData = json.decode(response.body); debugPrint('Decoded JSON: $jsonData'); final List resultList = jsonData['result'] as List; debugPrint('Found ${resultList.length} items'); if (resultList.isNotEmpty) { debugPrint('First item data: ${resultList.first}'); } _allMonthlyItems = resultList.map((item) => MonthlyItem.fromJson(item)).toList(); if (_allMonthlyItems.isNotEmpty) { debugPrint('First item link: ${_allMonthlyItems.first.link}'); debugPrint('First item file: ${_allMonthlyItems.first.file}'); } monthlyItems = _applyFilters(_allMonthlyItems); debugPrint( 'Successfully parsed ${monthlyItems.length} monthly items after filters'); appState = AppState.idle; } else { debugPrint('Request failed with status code: ${response.statusCode}'); appState = AppState.failed; } } catch (e, stackTrace) { debugPrint('Error fetching monthly list: $e'); debugPrint('Stack trace: $stackTrace'); appState = AppState.failed; } notifyListeners(); } void search(String query) { searchQuery = query; var filtered = _allMonthlyItems; if (query.isNotEmpty) { filtered = filtered .where((item) => item.title.toLowerCase().contains(query.toLowerCase()) || item.description.toLowerCase().contains(query.toLowerCase())) .toList(); } monthlyItems = _applyFilters(filtered); notifyListeners(); } Future toggleLike(int id) async { try { final url = '${RequestHelper.baseUrl}/monthly/$id/like'; debugPrint('Toggling like for monthly item: $url'); final index = monthlyItems.indexWhere((item) => item.id == id); if (index != -1) { monthlyItems[index].isLiked = !monthlyItems[index].isLiked; notifyListeners(); } final response = await http.post( Uri.parse(url), headers: { 'Authorization': 'Bearer ${RequestService.token}', 'accept': '*/*', 'Content-Type': 'application/json; charset=UTF-8', }, ).timeout(const Duration(seconds: 30)); if (response.statusCode == 200) { debugPrint('Like toggled successfully'); } else { debugPrint('Failed to toggle like: ${response.statusCode}'); if (index != -1) { monthlyItems[index].isLiked = !monthlyItems[index].isLiked; notifyListeners(); } } } catch (e) { debugPrint('Error toggling like: $e'); final index = monthlyItems.indexWhere((item) => item.id == id); if (index != -1) { monthlyItems[index].isLiked = !monthlyItems[index].isLiked; notifyListeners(); } } } Future toggleBookmark(int id) async { try { final url = '${RequestHelper.baseUrl}/monthly/$id/mark'; debugPrint('Toggling bookmark for monthly item: $url'); final index = monthlyItems.indexWhere((item) => item.id == id); if (index != -1) { monthlyItems[index].isBookmarked = !monthlyItems[index].isBookmarked; notifyListeners(); } final response = await http.post( Uri.parse(url), headers: { 'Authorization': 'Bearer ${RequestService.token}', 'accept': '*/*', 'Content-Type': 'application/json; charset=UTF-8', }, ).timeout(const Duration(seconds: 30)); if (response.statusCode == 200) { debugPrint('Bookmark toggled successfully'); } else { debugPrint('Failed to toggle bookmark: ${response.statusCode}'); if (index != -1) { monthlyItems[index].isBookmarked = !monthlyItems[index].isBookmarked; notifyListeners(); } } } catch (e) { debugPrint('Error toggling bookmark: $e'); final index = monthlyItems.indexWhere((item) => item.id == id); if (index != -1) { monthlyItems[index].isBookmarked = !monthlyItems[index].isBookmarked; notifyListeners(); } } } void resetFilters([bool notify = true]) { startDate = null; endDate = null; selectedCats.clear(); filtering = false; if (notify) { fetchMonthlyList(); } } List _applyFilters(List items) { var filtered = items.where((item) => item.file.isNotEmpty).toList(); if (startDate != null || endDate != null) { filtered = filtered.where((item) { final itemDate = DateTime.parse(item.publishedAt); if (startDate != null) { final start = DateTime.parse(startDate!); if (itemDate.isBefore(start)) return false; } if (endDate != null) { final end = DateTime.parse(endDate!).add(const Duration(days: 1)); if (itemDate.isAfter(end)) return false; } return true; }).toList(); } if (selectedCats.isNotEmpty) { filtered = filtered.where((item) { return item.tags .any((tag) => selectedCats.any((cat) => cat.id == tag.id)); }).toList(); } return filtered; } }