"""
STEP 1   Represent the New User.
Let's assume the new user rated:
[movie, rating] -> [2,4],[10,5],25,3],[50,4],[95,5]
The vector is:
[0,4,0,0,0,0,0,0,0,5,...,3,...,4,...,5] (100 movies)

STEP 2  Compute cosine Similarity to All Users
NewUser ↔ User 6
NewUser ↔ User 68
NewUser ↔ User 182
...

Result of new user:
[existing user, similarity] ->[6,0.62], [68,0.71]...

STEP 3  Choose k Nearest Neighbors
k=3
example result: 68,414,599 (3 nearest neighbors with
                            similar taste)

STEP 4  Find Movies They Liked
Now we look at: Movies rated highly by neighbors, but
Only movies the new user hasn't rated

For example:
movie    u68    u414  u599
32        5      4      5
47        4      5      4
83        5      5      4


⸻

STEP 5  Score Candidate Movies
We compute a weighted average using similarity with
the formula:
score(movie)=  sum(similarity_ratings)/sum(similarity)

With the above data we get:
moview      prediction
32             4.8
47             4.5
83             4.6

STEP 6  Recommend Top Movies
Recommended movies:
Movie 32
Movie 83
Movie 47



"""


import pandas as pd
from sklearn.metrics.pairwise import cosine_similarity

# Load matrix
matrix = pd.read_csv(
    "user_movie_matrix_10x100.csv",
    index_col=0
)

matrix_filled = matrix.fillna(0)

# ----------------------------------
# Create new user ratings sample
# ----------------------------------

new_user = [0]*100

new_user[1] = 4   # Movie 2
new_user[9] = 5   # Movie 10
new_user[24] = 3  # Movie 25
new_user[49] = 4  # Movie 50
new_user[94] = 5  # Movie 95

new_user_df = pd.DataFrame(
    [new_user],
    columns=matrix_filled.columns
)

# ----------------------------------
# Compute similarity
# ----------------------------------

similarities = cosine_similarity(
    new_user_df,
    matrix_filled
)[0]

sim_series = pd.Series(
    similarities,
    index=matrix.index
)

# ----------------------------------
# Select k nearest neighbors
# ----------------------------------

k = 3

nearest_users = sim_series.sort_values(
    ascending=False
).head(k)

# ----------------------------------
# Predict ratings
# ----------------------------------

neighbor_ratings = matrix.loc[
    nearest_users.index
]

weights = nearest_users.values

predicted_ratings = (
    neighbor_ratings.T.dot(weights)
    /
    weights.sum()
)

# Remove already rated movies
already_rated = [
    1,9,24,49,94
]

predicted_ratings.iloc[already_rated] = 0

# ----------------------------------
# Recommend top 5 movies [movie - average rating]
# ----------------------------------

recommendations = predicted_ratings.sort_values(
    ascending=False
).head(5)

print(recommendations)