import sounddevice as sd
import numpy as np
import librosa
import joblib
import firebase_admin
import time

from firebase_admin import credentials, db

# =====================================================
# FIREBASE SETUP
# =====================================================
cred = credentials.Certificate("serviceAccountKey.json")

firebase_admin.initialize_app(cred, {
    'databaseURL': 'https://tugas-akhir-9947b-default-rtdb.asia-southeast1.firebasedatabase.app/'
})

ref = db.reference("/Kebun")

# =====================================================
# LOAD MODEL & LABEL
# =====================================================
model = joblib.load("model_burung.pkl")

label_map = {
    10: "manusia",
    20: "burung"
}

# =====================================================
# PARAMETER
# =====================================================
duration = 2.0
fs = 22050

MIN_VOLUME = 0.06
MIN_CONFIDENCE = 0.75
MAX_DISTANCE = 300

# =====================================================
# HOLD DETEKSI & TIMER
# =====================================================
DETECTION_HOLD = 5
last_detection_time = 0
last_result = "tidak dikenal"

last_firebase_update = 0
firebase_interval = 2

print("===================================")
print("AI KNN BURUNG AKTIF")
print("Listening...")
print("===================================")

# =====================================================
# LOOP
# =====================================================
while True:
    try:
        # =============================================
        # AMBIL SENSOR FIREBASE
        # =============================================
        jarak = 0
        suara_sensor = 0

        try:
            sensor_data = ref.child("Sensor").get()
            if sensor_data:
                if "Jarak" in sensor_data:
                    jarak = int(sensor_data["Jarak"])
                if "Suara_dB" in sensor_data:
                    suara_sensor = float(sensor_data["Suara_dB"])
        except Exception as e:
            print("Gagal baca sensor:", e)

        # =============================================
        # RECORD AUDIO
        # =============================================
        audio = sd.rec(
            int(duration * fs),
            samplerate=fs,
            channels=1,
            device=1,
            dtype='float32'
        )
        sd.wait()
        audio = audio.flatten()

        # =============================================
        # HITUNG VOLUME & KONVERSI dB
        # =============================================
        volume = np.max(np.abs(audio))
        db_value = int(volume * 85)

        if db_value < 30:
            db_value = 30
        if db_value > 85:
            db_value = 85

        print("Volume:", round(volume, 4))
        print("dB:", db_value)

        # =============================================
        # DEFAULT
        # =============================================
        hasil = "tidak dikenal"
        confidence = 0.0

        # =============================================
        # DEBUG FILTER
        # =============================================
        print("========== FILTER ==========")
        print("Volume :", round(volume, 4))
        print("Min Volume :", MIN_VOLUME)
        print("Jarak :", jarak)
        print("Max Jarak :", MAX_DISTANCE)

        if volume < MIN_VOLUME:
            print("SKIP -> Volume terlalu kecil")
        if suara_sensor < 60:
            print("SKIP -> Suara sensor ESP32 kurang dari 60 dB")
        if jarak <= 2:
            print("SKIP -> Jarak terlalu dekat")
        if jarak > MAX_DISTANCE:
            print("SKIP -> Jarak terlalu jauh")

        # =============================================
        # FILTER NOISE & PREDIKSI
        # =============================================
        if (volume >= MIN_VOLUME and suara_sensor >= 60 and jarak > 2 and jarak <= MAX_DISTANCE):
            
            # NORMALISASI & MFCC
            audio = librosa.util.normalize(audio)
            mfcc = librosa.feature.mfcc(y=audio, sr=fs, n_mfcc=13)
            mfcc_scaled = np.mean(mfcc.T, axis=0).reshape(1, -1)

            # PREDIKSI
            proba = model.predict_proba(mfcc_scaled)[0]
            prediksi = model.predict(mfcc_scaled)[0]
            
            print("Probabilitas :", proba)
            confidence = float(np.max(proba))
            predicted_label = label_map.get(prediksi, "tidak dikenal")

            print("Raw Prediksi :", prediksi)
            print("Label :", predicted_label)
            print("Confidence :", round(confidence, 2))

            # =========================================
            # VALIDASI HASIL AI (Sudah di dalam posisi Indentasi yang Benar)
            # =========================================
            sorted_proba = np.sort(proba)
            highest = sorted_proba[-1]
            second = sorted_proba[-2]
            selisih = highest - second

            print("Selisih Proba :", round(selisih, 2))

            if (confidence >= 0.75 and selisih >= 0.20 and predicted_label in ["manusia", "burung"]):
                hasil = predicted_label
            else:
                hasil = "tidak dikenal"

        # =============================================
        # HOLD HASIL DETEKSI
        # =============================================
        current_time = time.time()

        if hasil in ["manusia", "burung"]:
            last_result = hasil
            last_detection_time = current_time

        # TAHAN HASIL
        if (current_time - last_detection_time) < DETECTION_HOLD:
            hasil_tampil = last_result
        else:
            hasil_tampil = "tidak dikenal"

        # =============================================
        # DEBUG TAMPILAN
        # =============================================
        print("Confidence:", round(confidence, 2))
        print("Hasil AI:", hasil)
        print("Tampil:", hasil_tampil)
        print("Jarak Firebase:", jarak)
        print("Suara Sensor:", suara_sensor)
        print("----------------")

        # =============================================
        # UPDATE FIREBASE
        # =============================================
        if (current_time - last_firebase_update) >= firebase_interval:
            last_firebase_update = current_time
            ref.update({
                "AI": {
                    "Klasifikasi": hasil_tampil,
                    "Confidence": round(confidence, 2),
                    "Volume": round(float(volume), 4)
                }
            })

        # =============================================
        # DELAY
        # =============================================
        time.sleep(0.3)

    except Exception as e:
        print("ERROR:", e)
        try:
            ref.update({
                "AI": {
                    "Klasifikasi": "error",
                    "Confidence": 0,
                    "Volume": 0
                }
            })
        except:
            pass
        time.sleep(1)