import pandas as pd
import numpy as np
from sklearn.preprocessing import StandardScaler
from sklearn.linear_model import Perceptron
from sklearn.model_selection import train_test_split
from sklearn.metrics import accuracy_score, precision_score, recall_score
import matplotlib.pyplot as plt

def perceptron_classifier_on_normalized_data(data):
    """
    Train a Perceptron classifier on data with normalization applied after splitting into training and testing sets.
    Evaluate its performance and visualize the decision boundary.

    Parameters:
    - data: A pandas DataFrame containing the dataset with features and target.

    Returns:
    - A dictionary containing the classifier's performance metrics.
    - Slope and intercept of the decision boundary.
    """

    # Split the data into training and testing sets first
    # TODO: Extract Feature_1 and Feature_2, then split the data
    features = data[['Feature_1', 'Feature_2']]
    target = data['Target']
    X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=42) #Do not change the random state

    # Normalize the training data and then apply the same transformation to the test data
    # Remember to prevent data leakage, only train/fit on training data, for testing data you just need to transform

    # Visualize the normalized training data
    # TODO: Plot a scatter plot of the normalized training data

    # Train the Perceptron classifier on the normalized training data
    # Initialize the Perceptron and fit it to the normalized training data
    perceptron = Perceptron(max_iter=1000, eta0=0.1, random_state=42)

    # Evaluate the classifier's performance on the normalized testing set
    # TODO: Make predictions on the normalized testing set and calculate performance metrics
    # Note: Convert the metrics to percentage(out of 100) and round them to the nearest integer
    metrics = {
        'accuracy': round(accuracy_score(y_test, y_pred) * 100),
        'precision': round(precision_score(y_test, y_pred) * 100),
        'recall': round(recall_score(y_test, y_pred) * 100),
    }

    
    # TODO: Calculate the coefficients and intercept from the Perceptron to determine the decision boundary
    slope = -perceptron.coef_[0, 0] / perceptron.coef_[0, 1]
    intercept = -perceptron.intercept_[0] / perceptron.coef_[0, 1]
     # Calculate the slope and intercept of the decision boundary (round to 2 decimal)
    slope = round(slope, 2)
    intercept = round(intercept, 2)

    # Visualize the decision boundary on the normalized training data
    plt.scatter(X_train_scaled[:, 0], X_train_scaled[:, 1], c=y_train, cmap=plt.cm.Paired)
    plt.title('Decision Boundary on Training Data')
    plt.xlabel('Feature_1 (Normalized)')
    plt.ylabel('Feature_2 (Normalized)')
    plt.plot(X_train_scaled[:, 0], slope * X_train_scaled[:, 0] + intercept, 'k-')
    plt.show()
    # TODO: Plot the decision boundary on a scatter plot of the normalized training data

    # Visualize the decision boundary on the normalized testing data
    # TODO: Plot the decision boundary on a scatter plot of the normalized testing data

    return metrics, slope, intercept


np.random.seed(42)
data = {
    'Feature_1': np.random.randn(100),
    'Feature_2': np.random.randn(100),
    'Target': np.random.choice([0, 1], size=100)
}
dataset = pd.DataFrame(data)

perceptron_classifier_on_normalized_data(dataset)