// lib/services/base_api_service.dart

import 'dart:convert';
import 'dart:io'; // Import for SocketException check
import 'dart:async'; // Import for TimeoutException check

import 'package:flutter/foundation.dart';
import 'package:http/http.dart' as http;
import 'package:shared_preferences/shared_preferences.dart';
import 'package:path/path.dart' as path;
import 'package:environment_monitoring_app/auth_provider.dart';

/// Custom exception thrown when the API returns a 401 Unauthorized status.
class SessionExpiredException implements Exception {
  final String message;
  SessionExpiredException([this.message = "Your session has expired. Please log in again."]);

  @override
  String toString() => message;
}

/// A low-level service for making direct HTTP requests.
/// This service is now "dumb" and only sends a request to the specific
/// baseUrl provided. It no longer contains logic for server fallbacks.
class BaseApiService {

  Future<Map<String, String>> _getHeaders() async {
    final prefs = await SharedPreferences.getInstance();
    final String? token = prefs.getString(AuthProvider.tokenKey);
    return {
      if (token != null) 'Authorization': 'Bearer $token',
    };
  }

  Future<Map<String, String>> _getJsonHeaders() async {
    final headers = await _getHeaders();
    headers['Content-Type'] = 'application/json';
    return headers;
  }

  /// Generic GET request handler.
  Future<Map<String, dynamic>> get(String baseUrl, String endpoint) async {
    try {
      final url = Uri.parse('$baseUrl/$endpoint');
      final response = await http.get(url, headers: await _getJsonHeaders())
          .timeout(const Duration(seconds: 60));
      return _handleResponse(response);
    } on SessionExpiredException {
      rethrow; // CRITICAL FIX: Allow SessionExpiredException to propagate up.
    } on SocketException catch (e) {
      debugPrint('BaseApiService GET network error: $e');
      rethrow; // Re-throw network-related exceptions to be caught by higher-level logic (e.g., login screen)
    } on TimeoutException catch (e) {
      debugPrint('BaseApiService GET timeout error: $e');
      rethrow; // Re-throw timeout exceptions
    } catch (e) {
      debugPrint('GET request to $baseUrl failed with general error: $e');
      return {'success': false, 'message': 'An unexpected error occurred: $e'};
    }
  }

  /// Generic POST request handler to a specific server.
  Future<Map<String, dynamic>> post(String baseUrl, String endpoint, Map<String, dynamic> body) async {
    try {
      final url = Uri.parse('$baseUrl/$endpoint');
      debugPrint('Attempting POST to: $url');
      final response = await http.post(
        url,
        headers: await _getJsonHeaders(),
        body: jsonEncode(body),
      ).timeout(const Duration(seconds: 60)); // Note: login.dart applies its own shorter timeout over this.
      return _handleResponse(response);
    } on SessionExpiredException {
      rethrow; // CRITICAL FIX: Allow SessionExpiredException to propagate up.
    } on SocketException catch (e) {
      debugPrint('BaseApiService POST network error: $e');
      rethrow; // Re-throw network-related exceptions to be caught by higher-level logic (e.g., login screen)
    } on TimeoutException catch (e) {
      debugPrint('BaseApiService POST timeout error: $e');
      rethrow; // Re-throw timeout exceptions
    } catch (e) {
      debugPrint('POST to $baseUrl failed with general error: $e');
      return {'success': false, 'message': 'API connection failed: $e'};
    }
  }

  /// Generic multipart request handler to a specific server.
  Future<Map<String, dynamic>> postMultipart({
    required String baseUrl,
    required String endpoint,
    required Map<String, String> fields,
    required Map<String, File> files,
  }) async {
    try {
      final url = Uri.parse('$baseUrl/$endpoint');
      debugPrint('Attempting multipart upload to: $url');

      var request = http.MultipartRequest('POST', url);
      final headers = await _getHeaders();
      request.headers.addAll(headers);

      if (fields.isNotEmpty) {
        request.fields.addAll(fields);
      }

      for (var entry in files.entries) {
        if (await entry.value.exists()) {
          request.files.add(await http.MultipartFile.fromPath(
            entry.key,
            entry.value.path,
            filename: path.basename(entry.value.path),
          ));
        } else {
          debugPrint('File does not exist: ${entry.value.path}. Skipping this file.');
        }
      }

      var streamedResponse = await request.send().timeout(const Duration(seconds: 60));
      final responseBody = await streamedResponse.stream.bytesToString();
      return _handleResponse(http.Response(responseBody, streamedResponse.statusCode));

    } on SessionExpiredException {
      rethrow; // CRITICAL FIX: Allow SessionExpiredException to propagate up.
    } on SocketException catch (e) {
      debugPrint('BaseApiService Multipart network error: $e');
      rethrow; // Re-throw network-related exceptions
    } on TimeoutException catch (e) {
      debugPrint('BaseApiService Multipart timeout error: $e');
      rethrow; // Re-throw timeout exceptions
    } catch (e, s) {
      debugPrint('Multipart upload to $baseUrl failed. Error: $e');
      debugPrint('Stack trace: $s');
      return {'success': false, 'message': 'API connection failed: $e'};
    }
  }

  Map<String, dynamic> _handleResponse(http.Response response) {
    debugPrint('Handling response. Status: ${response.statusCode}, Body: ${response.body}');

    // Check for 401 Unauthorized and throw the specific exception.
    if (response.statusCode == 401) {
      throw SessionExpiredException();
    }

    try {
      // Try to parse the response body as JSON.
      final Map<String, dynamic> responseData = jsonDecode(response.body);

      if (response.statusCode >= 200 && response.statusCode < 300) {
        // Successful API call (2xx status code)
        // Check application-level success flag if one exists in your API standard.
        // Assuming your API returns {'status': 'success', 'data': ...} or {'success': true, 'data': ...}
        if (responseData.containsKey('success') && responseData['success'] == false) {
          return {'success': false, 'message': responseData['message'] ?? 'API indicated failure.'};
        }
        // If no explicit failure flag, or if success=true, return success.
        // Adjust logic based on your API's specific response structure.
        return {
          'success': true,
          'data': responseData['data'], // Assumes data is nested under 'data' key
          'message': responseData['message'] ?? 'Success'
        };
      } else {
        // API returned an error code (4xx, 5xx).
        // Return the error message provided by the server.
        return {'success': false, 'message': responseData['message'] ?? 'Server error: ${response.statusCode}'};
      }
    } catch (e) {
      debugPrint('Failed to parse server response: $e');
      return {'success': false, 'message': 'Failed to parse server response. Body: ${response.body}'};
    }
  }
}