import 'dart:convert'; import 'package:flutter/material.dart'; import 'package:http/http.dart' as http; import 'package:shared_preferences/shared_preferences.dart'; class ApiService with ChangeNotifier { String? _baseUrl; String? _consumerKey; String? _consumerSecret; bool _isLoading = false; String? _error; bool get isLoading => _isLoading; String? get error => _error; bool get isLoggedIn => _baseUrl != null && _consumerKey != null && _consumerSecret != null; ApiService(); void connect(String url, String key, String secret) { _baseUrl = url; _consumerKey = key; _consumerSecret = secret; notifyListeners(); } Future verifyCredentials(String url, String key, String secret) async { _isLoading = true; _error = null; notifyListeners(); try { // Normalize URL String baseUrl = url.trim(); if (!baseUrl.startsWith('http')) baseUrl = 'https://$baseUrl'; if (baseUrl.endsWith('/')) baseUrl = baseUrl.substring(0, baseUrl.length - 1); // Construct WC API URL final apiUri = Uri.parse('$baseUrl/wp-json/wc/v3/system_status'); final response = await http.get( apiUri, headers: { 'Content-Type': 'application/json', 'Authorization': 'Basic ${base64Encode(utf8.encode('$key:$secret'))}', }, ); if (response.statusCode != 200) { String msg = 'Connection failed: ${response.statusCode}'; try { final body = jsonDecode(response.body); msg = body['message'] ?? msg; } catch (_) {} throw Exception(msg); } } catch (e) { _error = e.toString().contains('FormatException') ? 'Invalid Response. Is this a WooCommerce site?' : e.toString().replaceAll('Exception: ', ''); rethrow; } finally { _isLoading = false; notifyListeners(); } } void disconnect() { _baseUrl = null; _consumerKey = null; _consumerSecret = null; notifyListeners(); } Future logout() async { disconnect(); } // --- Products --- Map get _headers => { 'Content-Type': 'application/json', 'Authorization': 'Basic ${base64Encode(utf8.encode('$_consumerKey:$_consumerSecret'))}', }; // Helper to build WC API URIs Uri _getUri(String path, [Map? queryParams]) { // Ensuring path starts with /wp-json/wc/v3 // We assume _baseUrl is just the domain e.g. https://site.com // We append the internal implementation path return Uri.parse('$_baseUrl/wp-json/wc/v3$path').replace(queryParameters: queryParams); } Future> getProducts({int page = 1, String search = ''}) async { if (!isLoggedIn) throw Exception('Not logged in'); final uri = _getUri('/products', { 'page': page.toString(), 'per_page': '20', 'search': search, }); final response = await http.get(uri, headers: _headers); if (response.statusCode == 200) { try { return jsonDecode(response.body); } on FormatException { throw Exception('Invalid JSON response'); } } else { throw Exception('Failed to load products: ${response.statusCode}'); } } Future> getProduct(dynamic id) async { if (!isLoggedIn) throw Exception('Not logged in'); final uri = _getUri('/products/$id'); final response = await http.get(uri, headers: _headers); if (response.statusCode == 200) { return jsonDecode(response.body); } else { throw Exception('Failed to load product'); } } Future createProduct(Map productData) async { if (!isLoggedIn) throw Exception('Not logged in'); final uri = _getUri('/products'); final response = await http.post( uri, headers: _headers, body: jsonEncode(productData), ); if (response.statusCode != 200 && response.statusCode != 201) { throw Exception('Failed to create product: ${response.body}'); } } Future updateProduct(int id, Map productData) async { if (!isLoggedIn) throw Exception('Not logged in'); final uri = _getUri('/products/$id'); final response = await http.put( uri, headers: _headers, body: jsonEncode(productData), ); if (response.statusCode != 200) { throw Exception('Failed to update product: ${response.body}'); } } Future deleteProduct(int id) async { if (!isLoggedIn) throw Exception('Not logged in'); final uri = _getUri('/products/$id', {'force': 'true'}); final response = await http.delete(uri, headers: _headers); if (response.statusCode != 200) { throw Exception('Failed to delete product'); } } }