from flask import Flask, jsonify, request
import pandas as pd
import numpy as np
from prophet import Prophet

app = Flask(__name__)

# Nilai scale hasil pengujian grid search pada data pangkalan.
# Scale 0.4 dipilih karena menghasilkan nilai MAPE terkecil dibanding nilai scale lainnya.
# Nilai ini merepresentasikan bahwa setelah regulasi Oktober 2025, rata-rata penjualan
# pangkalan menjadi 40% dari kondisi sebelum regulasi.
PENGETATAN_SCALE = 0.4

# Threshold penurunan untuk mendeteksi apakah pangkalan terdampak pengetatan.
# Nilai 0.4 (40%) diperoleh dari data aktual pangkalan Eka yang menunjukkan
# penurunan sebesar 40% dari September 2025 (358 tabung) ke Oktober 2025 (215 tabung)
# akibat regulasi pengetatan distribusi LPG dari agen.
PENGETATAN_THRESHOLD = 0.4

@app.route("/")
def home():
    return jsonify({"status": "API PeGas Aktif", "mode": "Adaptive Local Scaling"})

@app.route("/predict", methods=['POST'])
def predict():
    try:
        data = request.json

        # 1. AMBIL PARAMETER DARI LARAVEL
        days = int(data.get('days', 30))          # jumlah hari prediksi (default 30)
        user_data_raw = data.get('user_data', []) # data dari Laravel

        # 2. PREPROCESSING DATA
        df_user = pd.DataFrame(user_data_raw)

        df_user['ds'] = pd.to_datetime(df_user['tanggal']) # ubah ke datetime
        df_user['y'] = df_user['jumlah']                   # target

        df_user = df_user[['ds', 'y']].sort_values('ds')   # urutkan berdasarkan tanggal

        # 3. DETEKSI APAKAH PANGKALAN TERDAMPAK PENGETATAN
        # Hitung rata-rata penjualan sebelum dan sesudah Oktober 2025
        before = df_user[df_user['ds'] < '2025-10-01']['y'].mean()  # rata-rata sebelum Oktober 2025
        after  = df_user[df_user['ds'] >= '2025-10-01']['y'].mean() # rata-rata setelah Oktober 2025

        # Cek apakah data lengkap dan terjadi penurunan signifikan
        # Pangkalan dianggap terdampak jika penurunan setelah Oktober 2025
        # mencapai 40% atau lebih dari kondisi sebelumnya, sesuai data aktual pangkalan.
        # Jika data sebelum atau sesudah Oktober 2025 tidak tersedia (NaN),
        # atau before bernilai 0 (menghindari pembagian nol), maka dianggap tidak terdampak.
        if pd.isna(before) or pd.isna(after) or before == 0:
            terdampak = False  # data tidak lengkap, anggap kondisi normal
        else:
            penurunan = (before - after) / before  # hitung persentase penurunan
            terdampak = penurunan >= PENGETATAN_THRESHOLD  # terdampak jika turun >= 40%

        print(f">>> Penurunan: {round(penurunan * 100, 1) if not pd.isna(before) and before != 0 else 'N/A'}%")
        print(f">>> Terdampak pengetatan: {terdampak}")

        # 4. BUAT REGRESSOR PENGETATAN
        # Penambahan variabel pengetatan_level sebagai regressor tambahan untuk
        # memberitahu model bahwa terdapat perubahan kondisi distribusi sejak Oktober 2025.
        # Nilai 1.0 merepresentasikan kondisi normal sebelum Oktober 2025.
        # Nilai PENGETATAN_SCALE (0.4) hanya diterapkan jika pangkalan terdeteksi terdampak,
        # konsisten dengan nilai yang digunakan pada saat pelatihan dan evaluasi model.
        df_user['pengetatan_level'] = 1.0  # default kondisi normal
        if terdampak:
            df_user.loc[df_user['ds'] >= '2025-10-01', 'pengetatan_level'] = PENGETATAN_SCALE
            print(f">>> Scale pengetatan diterapkan: {PENGETATAN_SCALE}")
        else:
            print(f">>> Pangkalan tidak terdampak pengetatan, scale tidak diterapkan")

        # 5. MODEL PROPHET
        # Konfigurasi seasonality disesuaikan dengan karakteristik data penjualan LPG:
        # - weekly_seasonality diaktifkan karena pola penjualan dipengaruhi jadwal pengiriman mingguan
        model = Prophet(
            yearly_seasonality=False,
            weekly_seasonality=True,
            daily_seasonality=False
        )

        # Tambahkan regressor pengetatan agar model mengetahui adanya perubahan kondisi eksternal.
        # Regressor harus ditambahkan sebelum model.fit().
        model.add_regressor('pengetatan_level')

        # 6. TRAIN MODEL
        model.fit(df_user)  # latih model menggunakan seluruh data historis pengguna

        # 7. FUTURE DATAFRAME
        future = model.make_future_dataframe(periods=days)  # buat daftar tanggal yang akan diprediksi

        # Isi nilai regressor untuk tanggal masa depan.
        # Nilai pengetatan_level harus diketahui untuk seluruh tanggal prediksi,
        # sesuai dengan ketentuan penggunaan additional regressor pada Prophet.
        future['pengetatan_level'] = 1.0  # default kondisi normal
        if terdampak:
            future.loc[future['ds'] >= '2025-10-01', 'pengetatan_level'] = PENGETATAN_SCALE

        # 8. PREDIKSI
        forecast = model.predict(future)  # prediksi masa depan dengan mempertimbangkan kondisi pengetatan

        # 9. HARI MINGGU = 0 (ATURAN BISNIS NYATA / agen libur pengiriman)
        # Setelah prediksi dihasilkan, dilakukan koreksi bahwa hari Minggu bernilai 0
        # karena agen tidak melakukan pengiriman pada hari tersebut.
        forecast['ds_dt'] = pd.to_datetime(forecast['ds'])
        is_sunday = forecast['ds_dt'].dt.dayofweek == 6  # 6 = hari Minggu

        forecast.loc[is_sunday, ['yhat', 'yhat_upper', 'yhat_lower']] = 0

        # 10. AMBIL HASIL AKHIR
        result = forecast[['ds', 'yhat', 'yhat_lower', 'yhat_upper']].tail(days).copy()

        # 11. POST-PROCESSING sebelum dikirim ke Laravel

        # Tidak boleh negatif karena penjualan LPG tidak mungkin bernilai minus
        result['yhat']       = result['yhat'].clip(lower=0)
        result['yhat_lower'] = result['yhat_lower'].clip(lower=0)
        result['yhat_upper'] = result['yhat_upper'].clip(lower=0)

        # Pembulatan karena satuan adalah tabung LPG yang tidak mungkin bernilai desimal
        result['yhat']       = result['yhat'].round(0).astype(int)
        result['yhat_lower'] = result['yhat_lower'].round(0).astype(int)
        result['yhat_upper'] = result['yhat_upper'].round(0).astype(int)

        # Format tanggal ke string agar bisa dikirim sebagai JSON ke Laravel
        result['ds'] = result['ds'].dt.strftime('%Y-%m-%d')

        # Kirim hasil prediksi ke Laravel dalam format JSON
        return jsonify(result.to_dict(orient="records"))

    except Exception as e:
        print(f"❌ Error pada server Flask: {str(e)}")
        return jsonify({"error": str(e)}), 500


if __name__ == "__main__":
    app.run(debug=True, port=5000)