import numpy as np
import skfuzzy as fuzz
from skfuzzy import control as ctrl
from flask import Flask, request, jsonify
from flask_cors import CORS

app = Flask(__name__)
CORS(app)

# Variabel Input & Output (Hanya untuk keperluan pembentukan grafik visual)
tinggi = ctrl.Antecedent(np.arange(0, 41, 0.1), "tinggi")
diameter = ctrl.Antecedent(np.arange(0, 3.1, 0.1), "diameter")
daun = ctrl.Antecedent(np.arange(0, 11, 1), "daun")
kualitas = ctrl.Consequent(np.arange(0, 101, 1), "kualitas")

tinggi["rendah"] = fuzz.trapmf(tinggi.universe, [0, 0, 15, 22])
tinggi["sedang"] = fuzz.trimf(tinggi.universe, [20, 24, 28])
tinggi["tinggi"] = fuzz.trapmf(tinggi.universe, [26, 35, 41, 41])

diameter["kecil"] = fuzz.trapmf(diameter.universe, [0, 0, 0.8, 1.2])
diameter["sedang"] = fuzz.trimf(diameter.universe, [1.0, 1.3, 1.6])
diameter["besar"] = fuzz.trapmf(diameter.universe, [1.4, 2.0, 3.1, 3.1])

daun["sedikit"] = fuzz.trapmf(daun.universe, [0, 0, 2, 4])
daun["cukup"] = fuzz.trimf(daun.universe, [3, 4.5, 6])
daun["banyak"] = fuzz.trapmf(daun.universe, [5, 8, 11, 11])

kualitas["tidak_unggul"] = fuzz.trimf(kualitas.universe, [0, 0, 60])
kualitas["unggul"] = fuzz.trimf(kualitas.universe, [50, 100, 100])


# FUNGSI DEFUZZIFIKASI MANUAL
# Menghitung Z* = (A1*Z1 + A2*Z2) / (A1 + A2)
def hitung_centroid_manual(alpha_tu, alpha_u):
    pembilang = 0.0
    penyebut = 0.0

    # 1. Perhitungan Luas dan Titik Pusat untuk Grafik "Tidak Unggul"
    if alpha_tu > 0:
        if alpha_tu == 1.0:
            A = 0.5 * 60.0 * 1.0
            Z = 60.0 / 3.0
            pembilang += A * Z
            penyebut += A
        else:
            z_potong = 60.0 - (60.0 * alpha_tu)
            # Area Persegi Panjang (kiri)
            A1 = z_potong * alpha_tu
            Z1 = z_potong / 2.0
            # Area Segitiga (kanan)
            A2 = 0.5 * (60.0 - z_potong) * alpha_tu
            Z2 = z_potong + ((60.0 - z_potong) / 3.0)

            pembilang += (A1 * Z1) + (A2 * Z2)
            penyebut += A1 + A2

    # 2. Perhitungan Luas dan Titik Pusat untuk Grafik "Unggul"
    if alpha_u > 0:
        if alpha_u == 1.0:
            A = 0.5 * 50.0 * 1.0
            Z = 50.0 + (50.0 / 3.0)
            pembilang += A * Z
            penyebut += A
        else:
            z_potong = 50.0 + (50.0 * alpha_u)
            # Area Segitiga (kiri)
            A1 = 0.5 * (z_potong - 50.0) * alpha_u
            Z1 = 50.0 + ((z_potong - 50.0) / 3.0)
            # Area Persegi Panjang (kanan)
            A2 = (100.0 - z_potong) * alpha_u
            Z2 = (z_potong + 100.0) / 2.0

            pembilang += (A1 * Z1) + (A2 * Z2)
            penyebut += A1 + A2

    if penyebut == 0:
        return 0.0

    return round(pembilang / penyebut, 2)


@app.route("/api/hitung-fuzzy", methods=["POST"])
def hitung_fuzzy():
    data = request.json
    try:
        val_t = float(data["tinggi"])
        val_d = float(data["diameter"])
        val_daun = float(data["daun"])

        # TAHAP 1: FUZZYFIKASI
        mu_t_r = float(
            fuzz.interp_membership(tinggi.universe, tinggi["rendah"].mf, val_t)
        )
        mu_t_s = float(
            fuzz.interp_membership(tinggi.universe, tinggi["sedang"].mf, val_t)
        )
        mu_t_t = float(
            fuzz.interp_membership(tinggi.universe, tinggi["tinggi"].mf, val_t)
        )

        mu_d_k = float(
            fuzz.interp_membership(diameter.universe, diameter["kecil"].mf, val_d)
        )
        mu_d_s = float(
            fuzz.interp_membership(diameter.universe, diameter["sedang"].mf, val_d)
        )
        mu_d_b = float(
            fuzz.interp_membership(diameter.universe, diameter["besar"].mf, val_d)
        )

        mu_daun_s = float(
            fuzz.interp_membership(daun.universe, daun["sedikit"].mf, val_daun)
        )
        mu_daun_c = float(
            fuzz.interp_membership(daun.universe, daun["cukup"].mf, val_daun)
        )
        mu_daun_b = float(
            fuzz.interp_membership(daun.universe, daun["banyak"].mf, val_daun)
        )

        # TAHAP 2: FUNGSI IMPLIKASI
        alpha_tu = 0.0
        alpha_u = 0.0
        aturan_aktif = []

        for t_key, t_val in [
            ("rendah", mu_t_r),
            ("sedang", mu_t_s),
            ("tinggi", mu_t_t),
        ]:
            for d_key, d_val in [
                ("kecil", mu_d_k),
                ("sedang", mu_d_s),
                ("besar", mu_d_b),
            ]:
                for daun_key, daun_val in [
                    ("sedikit", mu_daun_s),
                    ("cukup", mu_daun_c),
                    ("banyak", mu_daun_b),
                ]:

                    rule_act = min(t_val, d_val, daun_val)

                    if rule_act > 0:
                        if (
                            t_key == "rendah"
                            or d_key == "kecil"
                            or daun_key == "sedikit"
                        ):
                            consequent = "Tidak Unggul"
                            alpha_tu = max(alpha_tu, rule_act)
                        else:
                            consequent = "Unggul"
                            alpha_u = max(alpha_u, rule_act)

                        aturan_aktif.append(
                            {
                                "aturan": f"IF Tinggi {t_key.title()} ({t_val:.3f}) AND Diameter {d_key.title()} ({d_val:.3f}) AND Daun {daun_key.title()} ({daun_val:.3f})",
                                "hasil": consequent,
                                "nilai_min": round(rule_act, 3),
                            }
                        )

        tu_mf_array = kualitas["tidak_unggul"].mf
        u_mf_array = kualitas["unggul"].mf

        tu_implikasi = np.fmin(alpha_tu, tu_mf_array)
        u_implikasi = np.fmin(alpha_u, u_mf_array)

        # TAHAP 3: KOMPOSISI ATURAN
        komposisi_grafik = np.fmax(tu_implikasi, u_implikasi)

        # TAHAP 4: DEFUZZYFIKASI
        skor = hitung_centroid_manual(alpha_tu, alpha_u)

        # LOGIKA KESIMPULAN BERDASARKAN SKOR DEFUZZIFIKASI
        # Jika nilai akhir (Z*) >= 50, maka dikategorikan Unggul
        if skor >= 50:
            kesimpulan = "Unggul"
        else:
            kesimpulan = "Tidak Unggul"

        # PENGIRIMAN DATA KE FRONTEND
        return jsonify(
            {
                "status": "success",
                "skor": skor,
                "kesimpulan": kesimpulan,
                "fuzzifikasi": {
                    "tinggi": {
                        "rendah": round(mu_t_r, 3),
                        "sedang": round(mu_t_s, 3),
                        "tinggi": round(mu_t_t, 3),
                    },
                    "diameter": {
                        "kecil": round(mu_d_k, 3),
                        "sedang": round(mu_d_s, 3),
                        "besar": round(mu_d_b, 3),
                    },
                    "daun": {
                        "sedikit": round(mu_daun_s, 3),
                        "cukup": round(mu_daun_c, 3),
                        "banyak": round(mu_daun_b, 3),
                    },
                },
                "implikasi": {
                    "aturan_aktif": aturan_aktif,
                    "alpha_tidak_unggul": round(alpha_tu, 3),
                    "alpha_unggul": round(alpha_u, 3),
                },
                "grafik_data": {
                    "x": kualitas.universe.tolist(),
                    "tu_mf": tu_mf_array.tolist(),
                    "u_mf": u_mf_array.tolist(),
                    "aggregated": komposisi_grafik.tolist(),
                },
            }
        )
    except Exception as e:
        return jsonify({"status": "error", "message": str(e)})


if __name__ == "__main__":
    app.run(debug=True, port=5000)
