import 'dart:convert'; import 'dart:io'; import 'package:http/http.dart' as http; import '../../../../core/config/app_config.dart'; import '../../../../core/errors/exceptions.dart'; import '../../../../core/logging/app_logger.dart'; import '../models/task_model.dart'; abstract class TaskRemoteDataSource { Future> getTasksByDate(String date, {String? status}); Future getTaskById(String id); Future createTask(TaskModel task); Future updateTask(TaskModel task); Future deleteTask(String id); Future toggleTaskStatus(String id); Future rescheduleTask(String id, String targetDate); } class TaskRemoteDataSourceImpl implements TaskRemoteDataSource { final AppLogger logger; final http.Client _client; String get _baseUrl => AppConfig.current.apiBaseUrl; TaskRemoteDataSourceImpl({ required this.logger, required http.Client client, }) : _client = client; Map get _headers => { 'Content-Type': 'application/json', 'Accept': 'application/json', }; @override Future> getTasksByDate(String date, {String? status}) async { final queryParams = {'date': date}; if (status != null && status != 'all') { queryParams['status'] = status; } final uri = Uri.parse('$_baseUrl/tasks').replace(queryParameters: queryParams); logger.info('GET $uri'); try { final response = await _client.get(uri, headers: _headers); logger.debug('Response: ${response.statusCode}'); if (response.statusCode == 200) { final List jsonList = json.decode(response.body); return jsonList.map((json) => TaskModel.fromJson(json)).toList(); } else { throw ServerException( message: 'Failed to load tasks', statusCode: response.statusCode, ); } } on SocketException { throw const NetworkException(); } } @override Future getTaskById(String id) async { final uri = Uri.parse('$_baseUrl/tasks/$id'); logger.info('GET $uri'); try { final response = await _client.get(uri, headers: _headers); if (response.statusCode == 200) { return TaskModel.fromJson(json.decode(response.body)); } else if (response.statusCode == 404) { throw const NotFoundException(message: 'Task not found'); } else { throw ServerException( message: 'Failed to load task', statusCode: response.statusCode, ); } } on SocketException { throw const NetworkException(); } } @override Future createTask(TaskModel task) async { final uri = Uri.parse('$_baseUrl/tasks'); logger.info('POST $uri'); try { final response = await _client.post( uri, headers: _headers, body: json.encode(task.toCreateJson()), ); logger.debug('Response: ${response.statusCode}'); if (response.statusCode == 200 || response.statusCode == 201) { return TaskModel.fromJson(json.decode(response.body)); } else if (response.statusCode == 400) { throw ValidationException( message: 'Invalid task data', errors: json.decode(response.body), ); } else { throw ServerException( message: 'Failed to create task', statusCode: response.statusCode, ); } } on SocketException { throw const NetworkException(); } } @override Future updateTask(TaskModel task) async { final uri = Uri.parse('$_baseUrl/tasks/${task.id}'); logger.info('PUT $uri'); try { final response = await _client.put( uri, headers: _headers, body: json.encode(task.toJson()), ); if (response.statusCode == 200) { return TaskModel.fromJson(json.decode(response.body)); } else if (response.statusCode == 404) { throw const NotFoundException(message: 'Task not found'); } else { throw ServerException( message: 'Failed to update task', statusCode: response.statusCode, ); } } on SocketException { throw const NetworkException(); } } @override Future deleteTask(String id) async { final uri = Uri.parse('$_baseUrl/tasks/$id'); logger.info('DELETE $uri'); try { final response = await _client.delete(uri, headers: _headers); if (response.statusCode == 204 || response.statusCode == 200) { return; } else if (response.statusCode == 404) { throw const NotFoundException(message: 'Task not found'); } else { throw ServerException( message: 'Failed to delete task', statusCode: response.statusCode, ); } } on SocketException { throw const NetworkException(); } } @override Future toggleTaskStatus(String id) async { final uri = Uri.parse('$_baseUrl/tasks/$id/toggle'); logger.info('PATCH $uri'); try { final response = await _client.patch(uri, headers: _headers); if (response.statusCode == 200) { return TaskModel.fromJson(json.decode(response.body)); } else if (response.statusCode == 404) { throw const NotFoundException(message: 'Task not found'); } else { throw ServerException( message: 'Failed to toggle task status', statusCode: response.statusCode, ); } } on SocketException { throw const NetworkException(); } } @override Future rescheduleTask(String id, String targetDate) async { final uri = Uri.parse('$_baseUrl/tasks/$id/reschedule'); logger.info('POST $uri'); try { final response = await _client.post( uri, headers: _headers, body: json.encode({'target_date': targetDate}), ); if (response.statusCode == 200) { return TaskModel.fromJson(json.decode(response.body)); } else if (response.statusCode == 404) { throw const NotFoundException(message: 'Task not found'); } else { throw ServerException( message: 'Failed to reschedule task', statusCode: response.statusCode, ); } } on SocketException { throw const NetworkException(); } } }