import os
import traceback
from flask import Flask, request, jsonify
from flask_cors import CORS
import joblib
import pandas as pd

app = Flask(__name__)
CORS(app)

BASE_DIR = os.path.dirname(os.path.abspath(__file__))

MODEL_PATH = os.path.join(BASE_DIR, 'model_rekomendasi_15mapel.pkl')
ENCODER_PATH = os.path.join(BASE_DIR, 'encoders_15mapel.pkl')
FEATURE_COLUMNS_PATH = os.path.join(BASE_DIR, 'feature_columns_15mapel.pkl')

model = None
encoders = None
feature_columns = None


def load_artifacts():
    global model, encoders, feature_columns

    model = joblib.load(MODEL_PATH)
    encoders = joblib.load(ENCODER_PATH)
    feature_columns = joblib.load(FEATURE_COLUMNS_PATH)

    if not isinstance(feature_columns, list):
        raise ValueError("feature_columns_15mapel.pkl harus berisi list nama kolom.")

    required_encoder_keys = ['ekonomi_orang_tua', 'rekomendasi_jurusan']
    for key in required_encoder_keys:
        if key not in encoders:
            raise KeyError(f"Encoder '{key}' tidak ditemukan pada encoders_15mapel.pkl")


def safe_float(value, default=0.0):
    try:
        if value is None or value == '':
            return float(default)
        return float(value)
    except (ValueError, TypeError):
        return float(default)


def validate_required_features(data):
    if not isinstance(data, dict):
        return False, "Payload JSON tidak valid."

    missing = []
    for col in feature_columns:
        if col == 'ekonomi_orang_tua':
            continue
        if col not in data:
            missing.append(col)

    if 'ekonomi_orang_tua' not in data:
        missing.append('ekonomi_orang_tua')

    if missing:
        return False, f"Fitur berikut belum dikirim: {', '.join(missing)}"

    return True, None


try:
    load_artifacts()
    print("✅ API Flask siap. Model, encoder, dan feature columns berhasil dimuat.")
except Exception as e:
    print("❌ Gagal memuat aset machine learning.")
    print(str(e))


@app.route('/', methods=['GET'])
def home():
    return jsonify({
        'success': True,
        'message': 'Flask API Sistem Rekomendasi Jurusan aktif.'
    })


@app.route('/health', methods=['GET'])
def health():
    artifacts_ready = all([
        model is not None,
        encoders is not None,
        feature_columns is not None
    ])

    return jsonify({
        'success': artifacts_ready,
        'model_loaded': model is not None,
        'encoders_loaded': encoders is not None,
        'feature_columns_loaded': feature_columns is not None
    }), 200 if artifacts_ready else 500


@app.route('/predict', methods=['POST'])
def predict():
    global model, encoders, feature_columns

    try:
        if model is None or encoders is None or feature_columns is None:
            return jsonify({
                'success': False,
                'message': 'Aset model belum berhasil dimuat. Periksa file .pkl dan path-nya.'
            }), 500

        data = request.get_json(silent=True)
        if data is None:
            return jsonify({
                'success': False,
                'message': 'Request harus berformat JSON.'
            }), 400

        is_valid, error_message = validate_required_features(data)
        if not is_valid:
            return jsonify({
                'success': False,
                'message': error_message
            }), 400

        eko_text = str(data.get('ekonomi_orang_tua', 'Mampu')).strip()

        ekonomi_classes = list(encoders['ekonomi_orang_tua'].classes_)
        if eko_text not in ekonomi_classes:
            return jsonify({
                'success': False,
                'message': f"Nilai ekonomi_orang_tua tidak valid. Gunakan salah satu: {', '.join(ekonomi_classes)}"
            }), 400

        encoded_eko = encoders['ekonomi_orang_tua'].transform([eko_text])[0]

        input_dict = {}
        for col in feature_columns:
            if col == 'ekonomi_orang_tua':
                input_dict[col] = encoded_eko
            else:
                input_dict[col] = safe_float(data.get(col), 0)

        df_input = pd.DataFrame([input_dict], columns=feature_columns)

        pred_idx = model.predict(df_input)[0]
        nama_jurusan = encoders['rekomendasi_jurusan'].inverse_transform([pred_idx])[0]

        probabilities = None
        if hasattr(model, 'predict_proba'):
            try:
                proba = model.predict_proba(df_input)[0]
                class_labels = encoders['rekomendasi_jurusan'].inverse_transform(model.classes_)
                probabilities = [
                    {
                        'jurusan': str(label),
                        'probabilitas': round(float(score), 4)
                    }
                    for label, score in sorted(
                        zip(class_labels, proba),
                        key=lambda x: x[1],
                        reverse=True
                    )
                ]
            except Exception:
                probabilities = None

        return jsonify({
            'success': True,
            'prediksi_jurusan': str(nama_jurusan),
            'top_predictions': probabilities
        }), 200

    except Exception as e:
        traceback.print_exc()
        return jsonify({
            'success': False,
            'message': f'Terjadi kesalahan saat melakukan prediksi: {str(e)}'
        }), 500


if __name__ == '__main__':
    app.run(host='127.0.0.1', port=5000, debug=True)