import 'dart:convert'; import 'package:http/http.dart' as http; import 'package:get/get.dart'; import 'package:marco/controller/project_controller.dart'; import 'package:marco/helpers/services/api_endpoints.dart'; import 'package:marco/helpers/services/storage/local_storage.dart'; import 'package:marco/helpers/services/app_logger.dart'; import 'package:marco/helpers/services/auth_service.dart'; import 'package:marco/model/tenant/tenant_list_model.dart'; /// Abstract interface for tenant service functionality abstract class ITenantService { Future>?> getTenants({bool hasRetried = false}); Future selectTenant(String tenantId, {bool hasRetried = false}); } /// Tenant API service class TenantService implements ITenantService { static const String _baseUrl = ApiEndpoints.baseUrl; static const Map _headers = { 'Content-Type': 'application/json', }; /// Currently selected tenant static Tenant? currentTenant; /// Set the selected tenant static void setSelectedTenant(Tenant tenant) { currentTenant = tenant; } /// Check if tenant is selected static bool get isTenantSelected => currentTenant != null; /// Build authorized headers static Future> _authorizedHeaders() async { final token = await LocalStorage.getJwtToken(); if (token == null || token.isEmpty) { throw Exception('Missing JWT token'); } return {..._headers, 'Authorization': 'Bearer $token'}; } /// Handle API errors static void _handleApiError( http.Response response, dynamic data, String context) { final message = data['message'] ?? 'Unknown error'; final level = response.statusCode >= 500 ? LogLevel.error : LogLevel.warning; logSafe("❌ $context failed: $message [Status: ${response.statusCode}]", level: level); } /// Log exceptions static void _logException(dynamic e, dynamic st, String context) { logSafe("❌ $context exception", level: LogLevel.error, error: e, stackTrace: st); } @override Future>?> getTenants( {bool hasRetried = false}) async { try { final headers = await _authorizedHeaders(); logSafe("➡️ GET $_baseUrl/auth/get/user/tenants\nHeaders: $headers", level: LogLevel.info); final response = await http .get(Uri.parse("$_baseUrl/auth/get/user/tenants"), headers: headers); final data = jsonDecode(response.body); logSafe( "⬅️ Response: ${jsonEncode(data)} [Status: ${response.statusCode}]", level: LogLevel.info); if (response.statusCode == 200 && data['success'] == true) { logSafe("✅ Tenants fetched successfully."); return List>.from(data['data']); } if (response.statusCode == 401 && !hasRetried) { logSafe("⚠️ Unauthorized while fetching tenants. Refreshing token...", level: LogLevel.warning); final refreshed = await AuthService.refreshToken(); if (refreshed) return getTenants(hasRetried: true); logSafe("❌ Token refresh failed while fetching tenants.", level: LogLevel.error); return null; } _handleApiError(response, data, "Fetching tenants"); return null; } catch (e, st) { _logException(e, st, "Get Tenants API"); return null; } } @override Future selectTenant(String tenantId, {bool hasRetried = false}) async { try { final headers = await _authorizedHeaders(); logSafe( "➡️ POST $_baseUrl/auth/select-tenant/$tenantId\nHeaders: $headers", level: LogLevel.info); final response = await http.post( Uri.parse("$_baseUrl/auth/select-tenant/$tenantId"), headers: headers, ); final data = jsonDecode(response.body); logSafe( "⬅️ Response: ${jsonEncode(data)} [Status: ${response.statusCode}]", level: LogLevel.info); if (response.statusCode == 200 && data['success'] == true) { await LocalStorage.setJwtToken(data['data']['token']); await LocalStorage.setRefreshToken(data['data']['refreshToken']); logSafe("✅ Tenant selected successfully. Tokens updated."); // 🔥 Refresh projects when tenant changes try { final projectController = Get.find(); projectController.clearProjects(); projectController.fetchProjects(); } catch (_) { logSafe("⚠️ ProjectController not found while refreshing projects"); } return true; } if (response.statusCode == 401 && !hasRetried) { logSafe("⚠️ Unauthorized while selecting tenant. Refreshing token...", level: LogLevel.warning); final refreshed = await AuthService.refreshToken(); if (refreshed) return selectTenant(tenantId, hasRetried: true); logSafe("❌ Token refresh failed while selecting tenant.", level: LogLevel.error); return false; } _handleApiError(response, data, "Selecting tenant"); return false; } catch (e, st) { _logException(e, st, "Select Tenant API"); return false; } } }