import pandas as pd
import matplotlib.pyplot as plt
import seaborn as sns
from sklearn.model_selection import train_test_split
from sklearn.tree import DecisionTreeClassifier, plot_tree
from sklearn.metrics import accuracy_score, precision_score, recall_score, f1_score, confusion_matrix

df = pd.read_excel("loan.xlsx")

print("Dimensions: (rows, columns)", df.shape)
print("\nFirst 5 rows: \n", df.head())
print("\nMissing Values: \n", df.isnull().sum())
print("\nData types and non-null count\n", df.info())
print("\nStatistics:\n", df.describe(include="all").T)
print("\n\nColumns: ", df.columns)
df = df.dropna().drop_duplicates()

X = df.drop(columns = ["Loan_ID", "Loan_Status"], errors = "ignore")
y = df["Loan_Status"].map({"N": 0, "Y": 1})

print("Features:", X.columns)
print("Target: Loan_Status")

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

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

# 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)


model = DecisionTreeClassifier(
    criterion="gini", max_depth = 3, random_state=42
)

model.fit(X_train, y_train)

plt.figure(figsize=(14, 7))
plot_tree(
    model,
    feature_names = X_train.columns,
    class_names=["Rejected", "Approved"],
    filled=True
)
plt.show()

y_pred = model.predict(X_test)
print("Predictions: \n", y_pred)

print("Accuracy: ", accuracy_score(y_test, y_pred))
print("Precision: ", precision_score(y_test, y_pred, zero_division=0))
print("Recall: ", recall_score(y_test, y_pred, zero_division=0))
print("F1 Score: ", f1_score(y_test, y_pred, zero_division=0))
print("Confusion Matrix: \n")
cm = confusion_matrix(y_test, y_pred)
plt.figure(figsize=(14, 7))
sns.heatmap(cm, annot=True, fmt="d", cmap="Blues",
            xticklabels=["Class 0", "Class 1"],
            yticklabels=["Class 0", "Class 1"])

plt.title("Confusion Matrix")
plt.ylabel("Actual")
plt.xlabel("Predicted")

plt.show()


# ENHANCED: Training loan-status distribution
y_train.value_counts().sort_index().plot(kind="bar")
plt.xlabel("Loan status (0 = Rejected, 1 = Approved)")
plt.ylabel("Training applicants")
plt.title("Training Loan Class Distribution")
plt.show()
