import similarity from 'string-similarity';

import {
  addEntity as addScore,
  getEntity as getScore,
  updateEntity as updateScore,
} from '../../Services/Score';
import {
  getEntity as getActivity,
  updateEntity as updateActivity,
} from '../../Services/Activity';
import { getUser } from '../../Services/User';
import { notifyMatchedActivity } from '../../Services/Notifier';

const DEFAULT_VALUES = {
  distance: '50',
  unit: 'km',
};

const getDefinedUser = (user) => {
  const userIsDefined = user != null && user !== undefined;
  return userIsDefined;
};

const fillActivities = async (user) => {
  const activities = await Promise.all(user.activitiesID.map(getActivity));
  return { ...user, activities };
};

const hasActivities = user => user && user.activitiesID && user.activitiesID.length > 0;

const getCurrentUser = (users, key) => users
  .filter(getDefinedUser)
  .filter(user => user.userID === key);

const isNotCurrentUser = (user, key) => user.userID !== key;

const isRequest = activity => activity.type === 'request';

const isSerach = activity => activity.type === 'search';

const isActive = activity => activity.active;

const hasPositiveScore = (users, userID) => users.map(user => user.userID).includes(userID);

const hasSearchActivity = (user) => {
  if (user.activities && user.activities.length > 0) return false;

  return user.activities.filter(isSerach).length > 0;
};

const hasRequestActivity = user => user.activities.filter(isRequest).length > 0;

const getScoreActivities = (request, searchActivities) =>
  searchActivities.map(search => getScoreActivity(search, request));

const getScoreActivity = (search, request) => {
  const scoreName = similarity.compareTwoStrings(search.name, request.name);

  const scoreDescription = similarity.compareTwoStrings(search.description, request.description);

  const scoreActivity = similarity.compareTwoStrings(search.activity, request.activity);

  const scoreSkills = similarity.compareTwoStrings(
    search.skills.toString(),
    request.skills.toString()
  );

  const scoreSkillDescription = similarity.compareTwoStrings(
    search.skills.toString(),
    request.description
  );

  const scoreDescriptionSkill = similarity.compareTwoStrings(
    search.description,
    request.skills.toString()
  );

  const total = scoreName
  + scoreDescription
  + scoreActivity
  + scoreSkills
  + scoreSkillDescription
  + scoreDescriptionSkill;

  console.log('-------------------------------------------------------------------------');
  console.log(`[${search.userID} | ${request.userID}]`);
  console.log('-------------------------------------------------------------------------');
  console.log('scoreName:', scoreName);
  console.log('scoreDescription:', scoreDescription);
  console.log('scoreActivity:', scoreActivity);
  console.log('scoreSkills:', scoreSkills);
  console.log('scoreSkillDescription:', scoreSkillDescription);
  console.log('scoreDescriptionSkill:', scoreDescriptionSkill);
  console.log('SEARCH ACTIVITY ID:', search.activityID);
  console.log('REQUEST ACTIVITY ID:', request.activityID);
  console.log('-------------------------------------------------------------------------');
  console.log('TOTAL:', total / 6);
  console.log('-------------------------------------------------------------------------');
  console.log('*************************************************************************');

  return {
    score: total / 6,
    activitySearchID: search.activityID,
    activityRequestID: request.activityID,
    scoreID: search.activityID + request.activityID,
  };
};

export default () => ({
  execMatchingWorkflow: async (location, getCloseUsersLocations) => {
    const matchedUsers = await getCloseUsersLocations({
      id: location.collectionID,
      key: location.key,
      distance: location.distance || DEFAULT_VALUES.distance,
      unit: location.unit || DEFAULT_VALUES.unit,
    });

    if (!matchedUsers) return matchedUsers;

    const users = await Promise.all(matchedUsers
      .map(user => user.userName)
      .map(getUser));

    const currentUser = getCurrentUser(users, location.key)[0];

    if (!hasActivities(currentUser)) return matchedUsers;

    const currentUserWithSearchActivities = await fillActivities(currentUser);

    if (!hasSearchActivity(currentUserWithSearchActivities)) return matchedUsers;

    const usersWithActivities = await Promise.all(users
      .filter(getDefinedUser)
      .filter(user => isNotCurrentUser(user, location.key))
      .filter(hasActivities)
      .map(fillActivities));

    const usersWithRequestActivitiesActive = usersWithActivities
      .filter(hasRequestActivity);

    const userWithMatchedActivities = usersWithRequestActivitiesActive.map((user) => {
      const userScores = user.activities
        .filter(isRequest)
        .map((request) => {
          const requestScores = getScoreActivities(request, currentUserWithSearchActivities.activities);
          return requestScores;
        });
      const mergedUserScores = [].concat(...userScores);
      return { ...user, scores: mergedUserScores };
    });

    const userWithPositiveMatchedActivities = [].concat(...userWithMatchedActivities
      .map((user) => {
        const positivesScores = user.scores.filter(score => score.score > 0.5);
        return { ...user, scores: positivesScores };
      }));

    userWithPositiveMatchedActivities.forEach(user =>
      user.scores.forEach(async (score) => {
        const existingScore = await getScore(score.scoreID);

        const searchActivity = currentUserWithSearchActivities.activities
          .filter(activity => activity.activityID === score.activitySearchID)[0];
        const matchedActivitiesID = searchActivity.matchedActivitiesID
          ? searchActivity.matchedActivitiesID
          : [];

        if (!existingScore) {
          addScore(score);

          updateActivity({
            activityID: score.activitySearchID,
            matchedActivitiesID: [...matchedActivitiesID, score.activityRequestID],
          });

          notifyMatchedActivity(currentUserWithSearchActivities, searchActivity, user);
        } else if (score.socre !== existingScore.score) {
          updateScore(score);
        }
      }));

    return matchedUsers.filter(user => hasPositiveScore(userWithPositiveMatchedActivities, user.userName));
  },
  acceptToMeetWorkflow: async () => {
    // const requestActivity = usersWithRequestActivitiesActive
    //   .filter(activity => activity.activityID === score.activityRequestID)[0];
    // updateActivity({
    //   activityID: score.activityRequestID,
    //   matchedActivitiesID: [...requestActivity.matchedActivitiesID, score.activitySearchID],
    // });
  },
});
