import 'package:http/http.dart' as http;
import 'dart:async';
import 'dart:convert';

class MLService {
  // API URL - Change based on environment
  // Untuk emulator Android: 10.0.2.2
  // Untuk device fisik: 192.168.x.x atau 127.0.0.1 kalau local
  static const String baseUrl = 'http://10.0.2.2:5000';

  static const int timeoutSeconds = 30;

  /// Login using account data from MySQL login table.
  static Future<Map<String, dynamic>> login({
    required String usernameOrEmail,
    required String password,
  }) async {
    try {
      final response = await http
          .post(
            Uri.parse('$baseUrl/api/login'),
            headers: {'Content-Type': 'application/json'},
            body: jsonEncode({
              'email': usernameOrEmail,
              'username': usernameOrEmail,
              'password': password,
            }),
          )
          .timeout(Duration(seconds: timeoutSeconds));

      final body = jsonDecode(response.body) as Map<String, dynamic>;
      if (response.statusCode == 200) {
        return body;
      }

      return {'status': 'error', 'message': body['message'] ?? 'Login gagal'};
    } on TimeoutException {
      return {
        'status': 'error',
        'message':
            'Server tidak merespons. Pastikan Flask aktif dan IP/port API sudah benar.',
      };
    } catch (e) {
      return {
        'status': 'error',
        'message': 'Tidak dapat terhubung ke server: $e',
      };
    }
  }

  static Future<Map<String, dynamic>> sendForgotPasswordOtp({
    required String usernameOrEmail,
  }) async {
    return _postJson('/api/forgot-password/send-otp', {
      'email': usernameOrEmail,
      'username': usernameOrEmail,
    }, fallbackMessage: 'Gagal membuat OTP');
  }

  static Future<Map<String, dynamic>> verifyForgotPasswordOtp({
    required String usernameOrEmail,
    required String otp,
  }) async {
    return _postJson('/api/forgot-password/verify-otp', {
      'email': usernameOrEmail,
      'username': usernameOrEmail,
      'otp': otp,
    }, fallbackMessage: 'Gagal verifikasi OTP');
  }

  static Future<Map<String, dynamic>> resetPasswordWithOtp({
    required String usernameOrEmail,
    required String otp,
    required String newPassword,
  }) async {
    return _postJson('/api/forgot-password/reset', {
      'email': usernameOrEmail,
      'username': usernameOrEmail,
      'otp': otp,
      'new_password': newPassword,
    }, fallbackMessage: 'Gagal reset password');
  }

  static Future<Map<String, dynamic>> changePassword({
    required String usernameOrEmail,
    required String currentPassword,
    required String newPassword,
  }) async {
    return _postJson('/api/change-password', {
      'email': usernameOrEmail,
      'username': usernameOrEmail,
      'current_password': currentPassword,
      'new_password': newPassword,
    }, fallbackMessage: 'Gagal mengubah password');
  }

  static Future<Map<String, dynamic>> _postJson(
    String path,
    Map<String, dynamic> payload, {
    required String fallbackMessage,
  }) async {
    try {
      final response = await http
          .post(
            Uri.parse('$baseUrl$path'),
            headers: {'Content-Type': 'application/json'},
            body: jsonEncode(payload),
          )
          .timeout(Duration(seconds: timeoutSeconds));

      final body = jsonDecode(response.body) as Map<String, dynamic>;
      if (response.statusCode >= 200 && response.statusCode < 300) {
        return body;
      }

      return {'status': 'error', 'message': body['message'] ?? fallbackMessage};
    } on TimeoutException {
      return {
        'status': 'error',
        'message':
            'Server tidak merespons. Pastikan Flask aktif dan IP/port API sudah benar.',
      };
    } catch (e) {
      return {
        'status': 'error',
        'message': 'Tidak dapat terhubung ke server: $e',
      };
    }
  }

  /// Convert grams to kilograms with rounding rules:
  /// - <= 500g -> 0.5kg
  /// - 500g < g <= 1000g -> 1kg
  /// - > 1000g -> round up to nearest 0.5kg
  static double gramsToKgRounded(num grams) {
    if (grams <= 0) {
      throw ArgumentError('grams must be greater than 0');
    }
    if (grams <= 500) return 0.5;
    if (grams <= 1000) return 1.0;
    return (grams / 500).ceilToDouble() * 0.5;
  }

  /// Health Check - Test if API is running
  static Future<bool> healthCheck() async {
    try {
      final response = await http
          .get(Uri.parse('$baseUrl/health'))
          .timeout(Duration(seconds: 5));
      return response.statusCode == 200;
    } catch (e) {
      print('Health check error: $e');
      return false;
    }
  }

  /// Get Metadata - Retrieve model information
  static Future<Map<String, dynamic>> getMetadata() async {
    try {
      final response = await http
          .get(Uri.parse('$baseUrl/metadata'))
          .timeout(Duration(seconds: timeoutSeconds));

      if (response.statusCode == 200) {
        return jsonDecode(response.body);
      } else {
        return {'status': 'error', 'message': 'Failed to get metadata'};
      }
    } catch (e) {
      return {'status': 'error', 'message': 'Connection error: $e'};
    }
  }

  /// Simplified Prediction - For UI use
  static Future<Map<String, dynamic>> simplePrediksi({
    required String productName,
    required String category,
    required int unitPrice,
    DateTime? predictionDate,
  }) async {
    try {
      final date = predictionDate ?? DateTime.now();

      // Map product names to encoded values (adjust based on your encoding)
      final productMap = {
        'Tepung Terigu 1kg': 1,
        'Telur 1kg': 2,
        'Gula Pasir 1kg': 3,
        'Susu Bubuk': 4,
        'Cokelat Bubuk 250gr': 5,
        'Mentega 500gr': 6,
        'Keju Parut 250gr': 7,
        'Baking Powder': 8,
      };

      final categoryMap = {
        'Tepung': 1,
        'Telur': 2,
        'Gula': 3,
        'Susu': 4,
        'Cokelat': 5,
        'Mentega': 6,
        'Keju': 7,
        'Bahan Tambahan': 8,
      };

      final produkEncoded = productMap[productName] ?? 1;
      final kategoriEncoded = categoryMap[category] ?? 1;

      return await prediksiStok(
        tahun: date.year,
        bulan: date.month,
        hari: date.day,
        hariDalamMinggu: date.weekday,
        hariMinggu: date.weekday,
        hargaSatuanUpdate: unitPrice,
        totalHargaUpdate: unitPrice, // Simplified
        produkEncoded: produkEncoded,
        namaProdukEncoded: produkEncoded,
        kategoriProdukEncoded: kategoriEncoded,
      );
    } catch (e) {
      return {'status': 'error', 'message': 'Connection error: $e'};
    }
  }

  /// Single Prediction - Predict stock demand
  static Future<Map<String, dynamic>> prediksiStok({
    required int tahun,
    required int bulan,
    required int hari,
    required int hariDalamMinggu,
    required int hariMinggu,
    required int hargaSatuanUpdate,
    required int totalHargaUpdate,
    required int produkEncoded,
    required int namaProdukEncoded,
    required int kategoriProdukEncoded,
  }) async {
    try {
      final data = {
        'tahun': tahun,
        'bulan': bulan,
        'hari': hari,
        'hari_dalam_minggu': hariDalamMinggu,
        'hari_minggu': hariMinggu,
        'harga_satuan_update': hargaSatuanUpdate,
        'total_harga_update': totalHargaUpdate,
        'produk_encoded': produkEncoded,
        'nama_produk_encoded': namaProdukEncoded,
        'kategori_produk_encoded': kategoriProdukEncoded,
      };

      final response = await http
          .post(
            Uri.parse('$baseUrl/prediksi'),
            headers: {'Content-Type': 'application/json'},
            body: jsonEncode(data),
          )
          .timeout(Duration(seconds: timeoutSeconds));

      if (response.statusCode == 200) {
        return jsonDecode(response.body);
      } else {
        return {
          'status': 'error',
          'message': 'Server error: ${response.statusCode}',
        };
      }
    } catch (e) {
      return {'status': 'error', 'message': 'Connection error: $e'};
    }
  }

  /// Batch Prediction - Predict multiple items
  static Future<Map<String, dynamic>> batchPrediksi(
    List<Map<String, dynamic>> items,
  ) async {
    try {
      final data = {'items': items};

      final response = await http
          .post(
            Uri.parse('$baseUrl/batch-prediksi'),
            headers: {'Content-Type': 'application/json'},
            body: jsonEncode(data),
          )
          .timeout(Duration(seconds: timeoutSeconds));

      if (response.statusCode == 200) {
        return jsonDecode(response.body);
      } else {
        return {
          'status': 'error',
          'message': 'Server error: ${response.statusCode}',
        };
      }
    } catch (e) {
      return {'status': 'error', 'message': 'Connection error: $e'};
    }
  }

  /// Get API Info
  static Future<Map<String, dynamic>> getInfo() async {
    try {
      final response = await http
          .get(Uri.parse('$baseUrl/info'))
          .timeout(Duration(seconds: timeoutSeconds));

      if (response.statusCode == 200) {
        return jsonDecode(response.body);
      } else {
        return {'status': 'error', 'message': 'Failed to get info'};
      }
    } catch (e) {
      return {'status': 'error', 'message': 'Connection error: $e'};
    }
  }

  // ========================================================================
  // DASHBOARD ENDPOINTS
  // ========================================================================

  /// Ringkasan dashboard: penggunaan bahan
  static Future<Map<String, dynamic>> getDashboardSummary() async {
    try {
      final response = await http
          .get(Uri.parse('$baseUrl/api/dashboard/summary'))
          .timeout(Duration(seconds: timeoutSeconds));

      if (response.statusCode == 200) {
        return jsonDecode(response.body);
      }

      return {
        'status': false,
        'message': 'Server error: ${response.statusCode}',
        'penggunaan_bahan': [],
      };
    } catch (e) {
      return {
        'status': false,
        'message': 'Connection error: $e',
        'penggunaan_bahan': [],
      };
    }
  }

  /// Total bahan keluar yang digunakan hari ini.
  static Future<Map<String, dynamic>> getBahanDigunakanHariIni() async {
    const fallback = {
      'total': 0,
      'satuan': 'kg',
      'keterangan': 'total penggunaan hari ini',
    };

    try {
      final response = await http
          .get(Uri.parse('$baseUrl/api/dashboard/bahan-digunakan-hari-ini'))
          .timeout(Duration(seconds: timeoutSeconds));

      if (response.statusCode == 200) {
        final data = jsonDecode(response.body);
        return data is Map<String, dynamic> ? data : fallback;
      }

      return {
        ...fallback,
        'status': false,
        'message': 'Server error: ${response.statusCode}',
      };
    } catch (e) {
      return {...fallback, 'status': false, 'message': 'Connection error: $e'};
    }
  }

  // ========================================================================
  // REPORT ENDPOINTS - LAPORAN
  // ========================================================================

  /// Helper GET untuk endpoint laporan (response: {status, message, data})
  static Future<Map<String, dynamic>> _getReport(String path) async {
    try {
      final response = await http
          .get(Uri.parse('$baseUrl$path'))
          .timeout(Duration(seconds: timeoutSeconds));

      if (response.statusCode == 200) {
        return jsonDecode(response.body);
      }

      print('Report request failed [$path]: ${response.statusCode}');
      return {
        'status': false,
        'message': 'Server error: ${response.statusCode}',
        'data': [],
      };
    } catch (e) {
      print('Report request error [$path]: $e');
      return {'status': false, 'message': 'Connection error: $e', 'data': []};
    }
  }

  /// Laporan stok bahan
  static Future<Map<String, dynamic>> getReportStock() async {
    return _getReport('/laporan/stok');
  }

  /// Riwayat stok masuk
  static Future<Map<String, dynamic>> getReportStockIn() async {
    return _getReport('/laporan/stok-masuk');
  }

  /// Laporan prediksi permintaan
  static Future<Map<String, dynamic>> getReportPredictions() async {
    return _getReport('/laporan/prediksi');
  }

  /// Laporan bahan kritis
  static Future<Map<String, dynamic>> getReportCritical() async {
    return _getReport('/laporan/bahan-kritis');
  }

  // ========================================================================
  // DATABASE ENDPOINTS - PRODUCTS & TRANSACTIONS
  // ========================================================================

  /// Consume stock (batch) after production
  static Future<Map<String, dynamic>> consumeStock({
    required List<Map<String, dynamic>> items,
    String? recipeName,
    int? productionQuantity,
  }) async {
    try {
      final data = {
        'items': items,
        if (recipeName != null) 'recipe_name': recipeName,
        if (productionQuantity != null)
          'production_quantity': productionQuantity,
      };

      print('[consumeStock] Request payload: ${jsonEncode(data)}');

      final response = await http
          .post(
            Uri.parse('$baseUrl/stock/consume'),
            headers: {'Content-Type': 'application/json'},
            body: jsonEncode(data),
          )
          .timeout(Duration(seconds: timeoutSeconds));

      print('[consumeStock] Response status: ${response.statusCode}');
      print('[consumeStock] Response body: ${response.body}');

      final parsedBody =
          response.body.isNotEmpty ? jsonDecode(response.body) : null;

      if (response.statusCode == 200) {
        return parsedBody is Map<String, dynamic>
            ? parsedBody
            : {'status': 'success'};
      }

      if (parsedBody is Map<String, dynamic>) {
        return parsedBody;
      }

      return {
        'status': 'error',
        'message': 'Server error: ${response.statusCode}',
      };
    } catch (e) {
      print('Consume stock error: $e');
      return {'status': 'error', 'message': 'Connection error: $e'};
    }
  }

  /// Get all products from database
  static Future<List<Map<String, dynamic>>> getProducts() async {
    try {
      final response = await http
          .get(Uri.parse('$baseUrl/products'))
          .timeout(Duration(seconds: timeoutSeconds));

      if (response.statusCode == 200) {
        final data = jsonDecode(response.body);
        if (data['status'] == 'success') {
          return List<Map<String, dynamic>>.from(data['products']);
        }
      }
      return [];
    } catch (e) {
      print('Get products error: $e');
      return [];
    }
  }

  /// Create new product in database
  static Future<Map<String, dynamic>> createProduct({
    required String name,
    required String category,
    required int price,
    required int currentStock,
    double? minStock,
    String? unit,
    String? productType,
  }) async {
    try {
      final data = {
        'name': name,
        'category': category,
        'price': price,
        'current_stock': currentStock,
        if (minStock != null) 'min_stock': minStock,
        if (unit != null) 'unit': unit,
        if (productType != null) 'product_type': productType,
      };

      final response = await http
          .post(
            Uri.parse('$baseUrl/products'),
            headers: {'Content-Type': 'application/json'},
            body: jsonEncode(data),
          )
          .timeout(Duration(seconds: timeoutSeconds));

      if (response.statusCode == 201) {
        return jsonDecode(response.body);
      } else if (response.statusCode == 409) {
        final result = jsonDecode(response.body);
        return result;
      } else if (response.statusCode == 400) {
        final result = jsonDecode(response.body);
        return {
          'status': 'error',
          'message': result['message'] ?? 'Bad request',
        };
      } else {
        print('Create product error - Status: ${response.statusCode}');
        print('Response body: ${response.body}');
        return {
          'status': 'error',
          'message': 'Server error: ${response.statusCode}',
        };
      }
    } catch (e) {
      print('Create product exception: $e');
      return {'status': 'error', 'message': 'Connection error: $e'};
    }
  }

  /// Get specific product by ID
  static Future<Map<String, dynamic>?> getProduct(int productId) async {
    try {
      final response = await http
          .get(Uri.parse('$baseUrl/products/$productId'))
          .timeout(Duration(seconds: timeoutSeconds));

      if (response.statusCode == 200) {
        final data = jsonDecode(response.body);
        if (data['status'] == 'success') {
          return data['product'];
        }
      }
      return null;
    } catch (e) {
      print('Get product error: $e');
      return null;
    }
  }

  /// Save transaction and update stock automatically
  static Future<Map<String, dynamic>> addTransactionWithStockUpdate({
    required int productId,
    required int quantity,
    required int unitPrice,
    required int totalPrice,
    required String transactionDate, // Format: 'YYYY-MM-DD'
  }) async {
    try {
      final data = {
        'produk_id': productId,
        'jumlah': quantity,
        'unit_price': unitPrice,
        'total_price': totalPrice,
        'transaction_date': transactionDate,
      };

      final response = await http
          .post(
            Uri.parse('$baseUrl/transaksi'),
            headers: {'Content-Type': 'application/json'},
            body: jsonEncode(data),
          )
          .timeout(Duration(seconds: timeoutSeconds));

      if (response.statusCode == 201) {
        final result = jsonDecode(response.body);
        return result;
      } else {
        return {
          'status': 'error',
          'message': 'Server error: ${response.statusCode}',
        };
      }
    } catch (e) {
      print('Add transaction with stock update error: $e');
      return {'status': 'error', 'message': 'Connection error: $e'};
    }
  }

  /// Save transaction to database (legacy - without stock update)
  static Future<bool> saveTransaction({
    required String productName,
    required String category,
    required int quantity,
    required int unitPrice,
    required int totalPrice,
    required String transactionDate, // Format: 'YYYY-MM-DD'
  }) async {
    try {
      final data = {
        'product_name': productName,
        'category': category,
        'quantity': quantity,
        'unit_price': unitPrice,
        'total_price': totalPrice,
        'transaction_date': transactionDate,
      };

      final response = await http
          .post(
            Uri.parse('$baseUrl/transactions'),
            headers: {'Content-Type': 'application/json'},
            body: jsonEncode(data),
          )
          .timeout(Duration(seconds: timeoutSeconds));

      if (response.statusCode == 201) {
        final result = jsonDecode(response.body);
        return result['status'] == 'success';
      }
      return false;
    } catch (e) {
      print('Save transaction error: $e');
      return false;
    }
  }

  /// Get transaction history from database
  static Future<List<Map<String, dynamic>>> getTransactions({
    int limit = 100,
    int offset = 0,
    String? productName,
  }) async {
    try {
      var url = '$baseUrl/transactions?limit=$limit&offset=$offset';
      if (productName != null && productName.isNotEmpty) {
        url += '&product_name=$productName';
      }

      final response = await http
          .get(Uri.parse(url))
          .timeout(Duration(seconds: timeoutSeconds));

      if (response.statusCode == 200) {
        final data = jsonDecode(response.body);
        if (data['status'] == 'success') {
          return List<Map<String, dynamic>>.from(data['transactions']);
        }
      }
      return [];
    } catch (e) {
      print('Get transactions error: $e');
      return [];
    }
  }

  /// Save prediction result to database
  static Future<bool> savePrediction({
    required String productName,
    required String category,
    required int unitPrice,
    required String predictionDate, // Format: 'YYYY-MM-DD'
    required int predictedQuantity,
    double? rawValue,
    int? estimatedTotalPrice,
    String? estimatedNeeds,
    double? accuracyR2,
    double? errorMae,
  }) async {
    try {
      final data = {
        'product_name': productName,
        'category': category,
        'unit_price': unitPrice,
        'prediction_date': predictionDate,
        'predicted_quantity': predictedQuantity,
        if (rawValue != null) 'raw_value': rawValue,
        if (estimatedTotalPrice != null)
          'estimated_total_price': estimatedTotalPrice,
        if (estimatedNeeds != null) 'estimated_needs': estimatedNeeds,
        if (accuracyR2 != null) 'accuracy_r2': accuracyR2,
        if (errorMae != null) 'error_mae': errorMae,
      };

      final response = await http
          .post(
            Uri.parse('$baseUrl/predictions'),
            headers: {'Content-Type': 'application/json'},
            body: jsonEncode(data),
          )
          .timeout(Duration(seconds: timeoutSeconds));

      if (response.statusCode == 201) {
        final result = jsonDecode(response.body);
        return result['status'] == 'success';
      }
      return false;
    } catch (e) {
      print('Save prediction error: $e');
      return false;
    }
  }

  // ========================================================================
  // RECIPES ENDPOINTS
  // ========================================================================

  /// Get all recipes with ingredients
  static Future<List<Map<String, dynamic>>> getRecipes() async {
    try {
      final response = await http
          .get(Uri.parse('$baseUrl/recipes'))
          .timeout(Duration(seconds: timeoutSeconds));

      if (response.statusCode == 200) {
        final data = jsonDecode(response.body);
        if (data['status'] == 'success') {
          return List<Map<String, dynamic>>.from(data['recipes']);
        }
      }
      return [];
    } catch (e) {
      print('Get recipes error: $e');
      return [];
    }
  }

  /// Get specific recipe by ID with ingredients
  static Future<Map<String, dynamic>?> getRecipe(int recipeId) async {
    try {
      final response = await http
          .get(Uri.parse('$baseUrl/recipes/$recipeId'))
          .timeout(Duration(seconds: timeoutSeconds));

      if (response.statusCode == 200) {
        final data = jsonDecode(response.body);
        if (data['status'] == 'success') {
          return data['recipe'];
        }
      }
      return null;
    } catch (e) {
      print('Get recipe error: $e');
      return null;
    }
  }
}
