import pandas as pd
import numpy as np
import matplotlib.pyplot as plt

from sklearn.model_selection import train_test_split
from sklearn.linear_model import LinearRegression
from sklearn.metrics import r2_score, mean_absolute_error, mean_squared_error

# ================================================
# 1. LOAD DATASET
# ================================================

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

print(df.head())
print("Shape:", df.shape)
print("Missing Values:\n", df.isnull().sum())
print("Statistics:\n", df.describe())

# ================================================
# 2. SELECT FEATURES AND CLEAN DATA
# ================================================

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

target = "Exam_Score"

df = df.dropna(subset=features + [target])

X = df[features]
y = df[target]

print("Input Features:", features)
print("Target:", target)

# ================================================
# 3. EDA - EXAMINE RELATIONSHIPS
# ================================================

for col in ["Hours_Studied", "Attendance", "Previous_Scores"]:
    plt.scatter(df[col], df["Exam_Score"], alpha=0.3)
    plt.xlabel(col)
    plt.ylabel("Final Exam Score")
    plt.title(col + " vs Exam Score")
    plt.show()

print("\nCorrelation with Exam Score:")
print(df[features + [target]].corr()["Exam_Score"])

# ================================================
# 4. TRAIN-TEST SPLIT
# ================================================

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

# ================================================
# 5. TRAIN LINEAR REGRESSION
# ================================================

model = LinearRegression()
model.fit(X_train, y_train)

# ================================================
# 6. PREDICT STUDENT SCORES
# ================================================

y_pred = model.predict(X_test)

comparison = pd.DataFrame({
    "Actual Score": y_test.values,
    "Predicted Score": y_pred
})

print("\nActual vs Predicted Scores:")
print(comparison.head(10))

# ================================================
# 7. EVALUATE MODEL
# ================================================

r2 = r2_score(y_test, y_pred)
mae = mean_absolute_error(y_test, y_pred)
rmse = np.sqrt(mean_squared_error(y_test, y_pred))

print("\nMODEL PERFORMANCE")
print("R2 Score:", r2)
print("MAE:", mae)
print("RMSE:", rmse)

# ================================================
# 8. FEATURES ASSOCIATED WITH PERFORMANCE
# ================================================

coefficients = pd.Series(model.coef_, index=features)

print("\nFeature Coefficients:")
print(coefficients)

print("\nAssociated with Higher Predicted Scores:")
print(coefficients[coefficients > 0])

print("\nAssociated with Lower Predicted Scores:")
print(coefficients[coefficients < 0])


# ENHANCED: Test residual histogram
residuals = y_test - y_pred
plt.hist(residuals, bins=20)
plt.xlabel("Actual - predicted score")
plt.title("Test Residual Distribution")
plt.show()
