import pandas as pd

from sklearn.model_selection import train_test_split
from sklearn.preprocessing import StandardScaler
from sklearn.metrics import accuracy_score, precision_score, recall_score, confusion_matrix

from tensorflow.keras.models import Sequential
from tensorflow.keras.layers import Input, Dense

# 1. LOAD DATASET

df = pd.read_csv("StudentPerformanceFactors.csv")

print(df.head())
print("Missing Values:\n", df.isnull().sum())

# CREATE PASS/FAIL TARGET
# 0 = Fail, 1 = Pass (70 is our example cutoff)

df["Pass"] = (df["Exam_Score"] >= 70).astype(int)

print("Pass/Fail Distribution:\n", df["Pass"].value_counts())

# 2. SELECT FEATURES AND ENCODE

features = ["Hours_Studied", "Attendance",
            "Previous_Scores", "Motivation_Level"]

df = df.dropna(subset=features + ["Exam_Score"])

X = df[features]
y = df["Pass"]

# Encode categories after splitting so only training columns define the schema.

# 3. TRAIN-TEST SPLIT

X_train, X_test, y_train, y_test = train_test_split(
    X, y, test_size=0.2, random_state=42, stratify=y
)

# Learn category choices from training rows only (also keeps dropped baselines consistent).
for column in X_train.select_dtypes(include=["object", "string", "category"]):
    categories = X_train[column].dropna().unique()
    X_train[column] = pd.Categorical(X_train[column], categories=categories)
    X_test[column] = pd.Categorical(X_test[column], categories=categories)
# Match test columns to training columns; unseen categories become zero.
X_train = pd.get_dummies(X_train, drop_first=False, dtype=int)
X_test = pd.get_dummies(X_test, drop_first=False, dtype=int).reindex(columns=X_train.columns, fill_value=0)


# 4. STANDARDIZATION

scaler = StandardScaler()
X_train = scaler.fit_transform(X_train)
X_test = scaler.transform(X_test)

# 5. BUILD ANN

model = Sequential([
    Input(shape=(X_train.shape[1],)),
    Dense(16, activation="relu"),
    Dense(8, activation="relu"),
    Dense(1, activation="sigmoid")
])

model.compile(optimizer="adam",
              loss="binary_crossentropy",
              metrics=["accuracy"])

model.fit(X_train, y_train, epochs=20, batch_size=32)

# 6. PREDICT AND EVALUATE

y_pred = (model.predict(X_test) > 0.5).astype(int).ravel()

print("Accuracy:", accuracy_score(y_test, y_pred))
print("Precision:", precision_score(y_test, y_pred))
print("Recall:", recall_score(y_test, y_pred))
print("Confusion Matrix:\n", confusion_matrix(y_test, y_pred))


# ENHANCED: Training pass/fail distribution and explicit cutoff explanation
import matplotlib.pyplot as plt
print("Target policy: Pass = 1 when Exam_Score >= 70; confirm this cutoff for your assignment.")
y_train.value_counts().sort_index().plot(kind="bar")
plt.xlabel("Pass (0 = Fail, 1 = Pass)")
plt.ylabel("Training students")
plt.title("Training Pass / Fail Distribution")
plt.show()
