import 'package:firebase_database/firebase_database.dart';

class FirebaseDatabaseService {
  static final FirebaseDatabaseService _instance =
      FirebaseDatabaseService._internal();

  factory FirebaseDatabaseService() => _instance;

  FirebaseDatabaseService._internal();

  final DatabaseReference _database = FirebaseDatabase.instance.ref();

  // ==================== SENSOR DATA ====================

  Stream<Map<String, dynamic>> getSensorDataStream() {
    return _database.child('data').onValue.map((event) {
      if (event.snapshot.value != null) {
        final data = Map<String, dynamic>.from(event.snapshot.value as Map);

        return {
          'suhu': _parseValue(data['suhu']),
          'kelembapan': _parseValue(data['kelembapan']),
          'ldr': _parseValue(data['ldr']),
          'water_flow': data['water_flow'] ?? 0,
          'soil_1': _parseValue(data['soil_1']),
          'soil_2': _parseValue(data['soil_2']),
          'soil_3': _parseValue(data['soil_3']),
          'soil_4': _parseValue(data['soil_4']),
          'soil_5': _parseValue(data['soil_5']),
        };
      }

      return {};
    });
  }

  Future<Map<String, dynamic>> getSensorData() async {
    try {
      final snapshot = await _database.child('data').get();

      if (snapshot.exists) {
        final data = Map<String, dynamic>.from(snapshot.value as Map);

        return {
          'suhu': _parseValue(data['suhu']),
          'kelembapan': _parseValue(data['kelembapan']),
          'ldr': _parseValue(data['ldr']),
          'water_flow': data['water_flow'] ?? 0,
          'soil_1': _parseValue(data['soil_1']),
          'soil_2': _parseValue(data['soil_2']),
          'soil_3': _parseValue(data['soil_3']),
          'soil_4': _parseValue(data['soil_4']),
          'soil_5': _parseValue(data['soil_5']),
        };
      }
    } catch (e) {
      print('Error getting sensor data: $e');
    }

    return {};
  }

  // ==================== AKTUATOR CONTROL ====================

  Stream<Map<String, bool>> getAktuatorStream() {
    return _database.child('aktuator').onValue.map((event) {
      if (event.snapshot.value != null) {
        final data = Map<String, dynamic>.from(event.snapshot.value as Map);

        return {
          'mosvet_1': data['mosvet_1'] ?? false,
          'mosvet_2': data['mosvet_2'] ?? false,
          'mosvet_3': data['mosvet_3'] ?? false,
          'mosvet_4': data['mosvet_4'] ?? false,
          'mosvet_5': data['mosvet_5'] ?? false,
          'mosvet_6': data['mosvet_6'] ?? false,
          'mosvet_7': data['mosvet_7'] ?? false,
          'mosvet_8': data['mosvet_8'] ?? false,
        };
      }

      return {};
    });
  }

  Future<void> setAktuator(String mosfetName, bool value) async {
    try {
      await _database.child('aktuator/$mosfetName').set(value);
    } catch (e) {
      print('Error setting aktuator: $e');
      rethrow;
    }
  }

  Future<void> setPompaAir(bool value) async {
    await setAktuator('mosvet_1', value);
  }

  Future<void> setPompaPupuk(bool value) async {
    await setAktuator('mosvet_2', value);
  }

  Future<void> setPengaduk(bool value) async {
    await setAktuator('mosvet_8', value);
  }

  Future<void> setPot(int potNumber, bool value) async {
    if (potNumber < 1 || potNumber > 5) {
      throw ArgumentError('Pot number must be between 1 and 5');
    }

    await setAktuator('mosvet_${potNumber + 2}', value);
  }

  Future<void> setMultipleAktuator(Map<String, bool> aktuatorStates) async {
    try {
      await _database.child('aktuator').update(aktuatorStates);
    } catch (e) {
      print('Error setting multiple aktuator: $e');
      rethrow;
    }
  }

  Future<void> turnOffAllAktuator() async {
    await setMultipleAktuator({
      'mosvet_1': false,
      'mosvet_2': false,
      'mosvet_3': false,
      'mosvet_4': false,
      'mosvet_5': false,
      'mosvet_6': false,
      'mosvet_7': false,
      'mosvet_8': false,
    });
  }

  // ==================== KONTROL CONFIG ====================

  Stream<Map<String, dynamic>> getKontrolStream() {
    return _database.child('kontrol').onValue.map((event) {
      if (event.snapshot.value != null) {
        return Map<String, dynamic>.from(event.snapshot.value as Map);
      }

      return {};
    });
  }

  Future<Map<String, dynamic>> getKontrolConfig() async {
    try {
      final snapshot = await _database.child('kontrol').get();

      if (snapshot.exists) {
        return Map<String, dynamic>.from(snapshot.value as Map);
      }
    } catch (e) {
      print('Error getting kontrol config: $e');
    }

    return {};
  }

  Future<void> updateKontrolConfig(Map<String, dynamic> config) async {
    try {
      await _database.child('kontrol').update(config);
    } catch (e) {
      print('Error updating kontrol config: $e');
      rethrow;
    }
  }

  Future<void> setThreshold({int? batasAtas, int? batasBawah}) async {
    try {
      final updates = <String, dynamic>{};

      if (batasAtas != null) updates['batas_atas'] = batasAtas;
      if (batasBawah != null) updates['batas_bawah'] = batasBawah;

      await _database.child('kontrol').update(updates);
    } catch (e) {
      print('Error setting threshold: $e');
      rethrow;
    }
  }

  Future<void> setWaktuPenyiraman({
    String? waktu1,
    String? waktu2,
    bool? waktuEnabled,
  }) async {
    try {
      final updates = <String, dynamic>{};

      if (waktu1 != null) updates['waktu_1'] = waktu1;
      if (waktu2 != null) updates['waktu_2'] = waktu2;
      if (waktuEnabled != null) updates['waktu'] = waktuEnabled;

      await _database.child('kontrol').update(updates);
    } catch (e) {
      print('Error setting waktu: $e');
      rethrow;
    }
  }

  Future<void> setDurasi({int? durasi1, int? durasi2}) async {
    try {
      final updates = <String, dynamic>{};

      if (durasi1 != null) updates['durasi_1'] = durasi1;
      if (durasi2 != null) updates['durasi_2'] = durasi2;

      await _database.child('kontrol').update(updates);
    } catch (e) {
      print('Error setting durasi: $e');
      rethrow;
    }
  }

  Future<void> setOtomatis(bool value) async {
    try {
      await _database.child('kontrol/otomatis').set(value);
    } catch (e) {
      print('Error setting otomatis: $e');
      rethrow;
    }
  }

  // ==================== HISTORY LOGGING BARU ====================

  Future<void> saveHistory(Map<String, dynamic> sensorData) async {
    try {
      final now = DateTime.now();

      for (int pot = 1; pot <= 5; pot++) {
        await _database.child('history').push().set({
          'timestamp': now.millisecondsSinceEpoch,
          'tanggal': '${now.day}/${now.month}/${now.year}',
          'waktu':
              '${now.hour.toString().padLeft(2, '0')}:${now.minute.toString().padLeft(2, '0')}',

          'jenis_histori': 'monitoring',
          'source': 'flutter_app',
          'mode': '-',
          'type': 'auto_log',

          'pot': pot,
          'nama_pot': 'Pot $pot',

          'suhu': _toDouble(sensorData['suhu']),
          'kelembapan': _toDouble(sensorData['kelembapan']),
          'ldr': _toDouble(sensorData['ldr']),
          'soil': _toDouble(sensorData['soil_$pot']),

          'durasi': 0,
        });
      }
    } catch (e) {
      print('Error saving history: $e');
    }
  }

  Future<void> saveManualWateringHistory({
    required List<int> pots,
    int duration = 0,
  }) async {
    try {
      if (pots.isEmpty) return;

      final now = DateTime.now();
      final sensorData = await getSensorData();

      for (final pot in pots) {
        await _database.child('history').push().set({
          'timestamp': now.millisecondsSinceEpoch,
          'tanggal': '${now.day}/${now.month}/${now.year}',
          'waktu':
              '${now.hour.toString().padLeft(2, '0')}:${now.minute.toString().padLeft(2, '0')}',

          'jenis_histori': 'penyiraman',
          'source': 'flutter_app',
          'mode': 'manual',
          'type': 'manual',

          'pot': pot,
          'nama_pot': 'Pot $pot',

          'suhu': _toDouble(sensorData['suhu']),
          'kelembapan': _toDouble(sensorData['kelembapan']),
          'ldr': _toDouble(sensorData['ldr']),
          'soil': _toDouble(sensorData['soil_$pot']),

          'durasi': duration,
        });
      }
    } catch (e) {
      print('Error saving manual watering history: $e');
    }
  }

  Future<void> saveMonitoringHistory() async {
    try {
      final sensorData = await getSensorData();
      await saveHistory(sensorData);
    } catch (e) {
      print('Error saving monitoring history: $e');
    }
  }

  Future<List<Map<String, dynamic>>> getHistoryListByDateRange(
    DateTime startDate,
    DateTime endDate,
  ) async {
    try {
      final start =
          DateTime(
            startDate.year,
            startDate.month,
            startDate.day,
          ).millisecondsSinceEpoch;

      final end =
          DateTime(
            endDate.year,
            endDate.month,
            endDate.day,
            23,
            59,
            59,
            999,
          ).millisecondsSinceEpoch;

      final snapshot =
          await _database
              .child('history')
              .orderByChild('timestamp')
              .startAt(start)
              .endAt(end)
              .get();

      if (!snapshot.exists) return [];

      final rawData = Map<String, dynamic>.from(snapshot.value as Map);
      final result = <Map<String, dynamic>>[];

      rawData.forEach((key, value) {
        if (value is Map) {
          final item = Map<String, dynamic>.from(value);
          item['id'] = key;
          result.add(item);
        }
      });

      result.sort((a, b) {
        final ta = int.tryParse(a['timestamp']?.toString() ?? '0') ?? 0;
        final tb = int.tryParse(b['timestamp']?.toString() ?? '0') ?? 0;
        return tb.compareTo(ta);
      });

      return result;
    } catch (e) {
      print('Error getting history list: $e');
      return [];
    }
  }

  Future<List<Map<String, dynamic>>> getLatestHistoryList({
    int limit = 100,
  }) async {
    try {
      final snapshot =
          await _database
              .child('history')
              .orderByChild('timestamp')
              .limitToLast(limit)
              .get();

      if (!snapshot.exists) return [];

      final rawData = Map<String, dynamic>.from(snapshot.value as Map);
      final result = <Map<String, dynamic>>[];

      rawData.forEach((key, value) {
        if (value is Map) {
          final item = Map<String, dynamic>.from(value);
          item['id'] = key;
          result.add(item);
        }
      });

      result.sort((a, b) {
        final ta = int.tryParse(a['timestamp']?.toString() ?? '0') ?? 0;
        final tb = int.tryParse(b['timestamp']?.toString() ?? '0') ?? 0;
        return tb.compareTo(ta);
      });

      return result;
    } catch (e) {
      print('Error getting latest history list: $e');
      return [];
    }
  }

  Future<void> clearOldHistory({int daysToKeep = 30}) async {
    try {
      final cutoff =
          DateTime.now()
              .subtract(Duration(days: daysToKeep))
              .millisecondsSinceEpoch;

      final snapshot = await _database.child('history').get();

      if (!snapshot.exists) return;

      final data = Map<String, dynamic>.from(snapshot.value as Map);

      for (final key in data.keys) {
        final item = data[key];

        if (item is Map) {
          final timestamp =
              int.tryParse(item['timestamp']?.toString() ?? '0') ?? 0;

          if (timestamp > 0 && timestamp < cutoff) {
            await _database.child('history/$key').remove();
          }
        }
      }
    } catch (e) {
      print('Error clearing old history: $e');
    }
  }

  // ==================== HELPER FUNCTIONS ====================

  String _parseValue(dynamic value) {
    if (value == null || value == '') {
      return '0';
    }

    return value.toString();
  }

  double _toDouble(dynamic value) {
    return double.tryParse(value?.toString() ?? '0') ?? 0;
  }

  DatabaseReference get databaseRef => _database;

  Stream<bool> getConnectionStatus() {
    return _database.child('.info/connected').onValue.map((event) {
      return event.snapshot.value as bool? ?? false;
    });
  }
}
