/// Remote Data Source: Reviews
///
/// Handles API calls for review operations.
library;

import 'package:dio/dio.dart';
import 'package:worker/core/errors/exceptions.dart';
import 'package:worker/core/network/dio_client.dart';
import 'package:worker/features/reviews/data/models/review_model.dart';
import 'package:worker/features/reviews/data/models/review_response_model.dart';
import 'package:worker/features/reviews/data/models/review_statistics_model.dart';

/// Reviews remote data source interface
abstract class ReviewsRemoteDataSource {
  /// Get reviews for a product (legacy - returns only reviews)
  Future<List<ReviewModel>> getProductReviews({
    required String itemId,
    int limitPageLength = 10,
    int limitStart = 0,
  });

  /// Get complete review response with statistics
  Future<ReviewResponseModel> getProductReviewsWithStats({
    required String itemId,
    int limitPageLength = 10,
    int limitStart = 0,
  });

  /// Submit a review (create or update)
  Future<void> submitReview({
    required String itemId,
    required double rating,
    required String comment,
    String? name,
  });

  /// Delete a review
  Future<void> deleteReview({required String name});
}

/// Reviews remote data source implementation
class ReviewsRemoteDataSourceImpl implements ReviewsRemoteDataSource {
  const ReviewsRemoteDataSourceImpl(this._dioClient);

  final DioClient _dioClient;

  // API endpoints
  static const String _getListEndpoint =
      '/api/method/building_material.building_material.api.item_feedback.get_list';
  static const String _updateEndpoint =
      '/api/method/building_material.building_material.api.item_feedback.update';
  static const String _deleteEndpoint =
      '/api/method/building_material.building_material.api.item_feedback.delete';

  @override
  Future<List<ReviewModel>> getProductReviews({
    required String itemId,
    int limitPageLength = 10,
    int limitStart = 0,
  }) async {
    try {
      final response = await _dioClient.post(
        _getListEndpoint,
        data: {
          'limit_page_length': limitPageLength,
          'limit_start': limitStart,
          'item_id': itemId,
        },
      );

      // Handle response
      if (response.statusCode == 200 || response.statusCode == 201) {
        final data = response.data;

        // Try different response formats
        List<dynamic> reviewsJson;

        if (data is Map<String, dynamic>) {
          // Frappe typically returns: { "message": {...} }
          if (data.containsKey('message')) {
            final message = data['message'];
            if (message is List) {
              reviewsJson = message;
            } else if (message is Map<String, dynamic>) {
              // New API format: { "message": { "feedbacks": [...], "statistics": {...} } }
              if (message.containsKey('feedbacks')) {
                reviewsJson = message['feedbacks'] as List;
              } else if (message.containsKey('data')) {
                // Alternative: { "message": { "data": [...] } }
                reviewsJson = message['data'] as List;
              } else {
                throw const ParseException(
                  'Unexpected response format: message map has no feedbacks or data key',
                );
              }
            } else {
              throw const ParseException(
                'Unexpected response format: message is not a list or map',
              );
            }
          } else if (data.containsKey('data')) {
            // Alternative: { "data": [...] }
            reviewsJson = data['data'] as List;
          } else if (data.containsKey('feedbacks')) {
            // Direct feedbacks key
            reviewsJson = data['feedbacks'] as List;
          } else {
            throw const ParseException(
              'Unexpected response format: no message, data, or feedbacks key',
            );
          }
        } else if (data is List) {
          // Direct list response
          reviewsJson = data;
        } else {
          throw ParseException(
            'Unexpected response format: ${data.runtimeType}',
          );
        }

        // Parse reviews
        return reviewsJson
            .map((json) => ReviewModel.fromJson(json as Map<String, dynamic>))
            .toList();
      } else {
        throw NetworkException(
          'Failed to fetch reviews',
          statusCode: response.statusCode,
          data: response.data,
        );
      }
    } on DioException catch (e) {
      throw _handleDioException(e);
    } on ParseException {
      rethrow;
    } catch (e, stackTrace) {
      throw UnknownException(
        'Failed to fetch reviews: ${e.toString()}',
        e,
        stackTrace,
      );
    }
  }

  @override
  Future<ReviewResponseModel> getProductReviewsWithStats({
    required String itemId,
    int limitPageLength = 10,
    int limitStart = 0,
  }) async {
    try {
      final response = await _dioClient.post(
        _getListEndpoint,
        data: {
          'limit_page_length': limitPageLength,
          'limit_start': limitStart,
          'item_id': itemId,
        },
      );

      // Handle response
      if (response.statusCode == 200 || response.statusCode == 201) {
        final data = response.data;

        // Parse the message object
        if (data is Map<String, dynamic> && data.containsKey('message')) {
          final message = data['message'];

          if (message is Map<String, dynamic>) {
            // New API format with complete response
            return ReviewResponseModel.fromJson(message);
          } else {
            throw const ParseException(
              'Unexpected response format: message is not a map',
            );
          }
        } else {
          throw const ParseException(
            'Unexpected response format: no message key',
          );
        }
      } else {
        throw NetworkException(
          'Failed to fetch reviews',
          statusCode: response.statusCode,
          data: response.data,
        );
      }
    } on DioException catch (e) {
      throw _handleDioException(e);
    } on ParseException {
      rethrow;
    } catch (e, stackTrace) {
      throw UnknownException(
        'Failed to fetch reviews: ${e.toString()}',
        e,
        stackTrace,
      );
    }
  }

  @override
  Future<void> submitReview({
    required String itemId,
    required double rating,
    required String comment,
    String? name,
  }) async {
    try {
      final data = {
        'item_id': itemId,
        'rating': rating,
        'comment': comment,
        if (name != null) 'name': name,
      };

      final response = await _dioClient.post(
        _updateEndpoint,
        data: data,
      );

      // Handle response
      if (response.statusCode != 200 && response.statusCode != 201) {
        throw NetworkException(
          'Failed to submit review',
          statusCode: response.statusCode,
          data: response.data,
        );
      }
    } on DioException catch (e) {
      throw _handleDioException(e);
    } catch (e, stackTrace) {
      throw UnknownException(
        'Failed to submit review: ${e.toString()}',
        e,
        stackTrace,
      );
    }
  }

  @override
  Future<void> deleteReview({required String name}) async {
    try {
      final response = await _dioClient.post(
        _deleteEndpoint,
        data: {'name': name},
      );

      // Handle response
      if (response.statusCode != 200 && response.statusCode != 204) {
        throw NetworkException(
          'Failed to delete review',
          statusCode: response.statusCode,
          data: response.data,
        );
      }
    } on DioException catch (e) {
      throw _handleDioException(e);
    } catch (e, stackTrace) {
      throw UnknownException(
        'Failed to delete review: ${e.toString()}',
        e,
        stackTrace,
      );
    }
  }

  /// Handle Dio exceptions and convert to app exceptions
  Exception _handleDioException(DioException e) {
    switch (e.type) {
      case DioExceptionType.connectionTimeout:
      case DioExceptionType.sendTimeout:
      case DioExceptionType.receiveTimeout:
        return const TimeoutException();

      case DioExceptionType.connectionError:
        return const NoInternetException();

      case DioExceptionType.badResponse:
        final statusCode = e.response?.statusCode;
        final data = e.response?.data;

        // Extract error message from response if available
        String? errorMessage;
        if (data is Map<String, dynamic>) {
          errorMessage = data['message'] as String? ??
              data['error'] as String? ??
              data['exc'] as String?;
        }

        switch (statusCode) {
          case 400:
            return BadRequestException(
              errorMessage ?? 'Dữ liệu không hợp lệ',
            );
          case 401:
            return UnauthorizedException(
              errorMessage ?? 'Phiên đăng nhập hết hạn. Vui lòng đăng nhập lại.',
            );
          case 403:
            return const ForbiddenException();
          case 404:
            return NotFoundException(
              errorMessage ?? 'Không tìm thấy đánh giá',
            );
          case 409:
            return ConflictException(
              errorMessage ?? 'Đánh giá đã tồn tại',
            );
          case 429:
            return const RateLimitException();
          case 500:
          case 502:
          case 503:
          case 504:
            return ServerException(
              errorMessage ?? 'Lỗi máy chủ. Vui lòng thử lại sau.',
              statusCode,
            );
          default:
            return NetworkException(
              errorMessage ?? 'Lỗi mạng không xác định',
              statusCode: statusCode,
              data: data,
            );
        }

      case DioExceptionType.cancel:
        return const NetworkException('Yêu cầu đã bị hủy');

      case DioExceptionType.badCertificate:
        return const NetworkException('Lỗi chứng chỉ SSL');

      case DioExceptionType.unknown:
      default:
        return NetworkException(
          'Lỗi kết nối: ${e.message ?? "Unknown error"}',
        );
    }
  }
}
