package controllers;

import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import flexjson.JSONSerializer;
import com.google.gson.JsonSerializer;
import models.contacts.Contact;
import models.contacts.Customer;
import models.contacts.FinancialInfo;
import models.contacts.Person;
import models.projects.Lead;
import models.projects.Project;
import org.joda.time.DateTime;
import org.joda.time.format.DateTimeFormat;
import org.joda.time.format.DateTimeFormatter;
import play.*;
import play.data.validation.MaxSize;
import play.data.validation.Validation;
import play.db.jpa.JPA;
import play.mvc.*;

import java.util.*;
import java.util.stream.Collectors;

import models.*;
import util.BCrypt;
import util.JSONModel;
import util.Search;
import util.Utils;


import javax.persistence.Query;

@With(Secure.class)
public class Application extends Controller {

    static final DateTimeFormatter DTFIN = DateTimeFormat.forPattern("dd-MM-yyyy");

    public static AppUser connectedUser() throws RuntimeException {
        String connected = Security.connected();
        if (connected == null || connected.isEmpty()) {
            //Secure.login();
            throw new RuntimeException("Not going to get here. For compiler warnings only.");
        }
        String trimmedUser = connected.toUpperCase().trim();
        AppUser user = AppUser.find("select au from AppUser au where UPPER(au.userName)=:username").setParameter("username", trimmedUser).first();
        // join fetch au.services join fetch au.asMember
        request.args.put("connectedUser", user);
        return user;
    }



    @Before
    public static void setConnectedUser() throws Throwable {
        String connected = Security.connected();
        if (connected == null || connected.isEmpty()) {
            Secure.login();
            throw new RuntimeException("Not going to get here. For compiler warnings only.");
        }
        String trimmedUser = connected.toUpperCase();
        Logger.info("searching for " + trimmedUser);
        AppUser user = AppUser.find("select au from AppUser au where UPPER(au.userName)=:username").setParameter("username", trimmedUser).first();

        // join fetch au.services join fetch au.asMember
        renderArgs.put("connectedUser", user);
        if(user != null) {
            Logger.info("As user : " + user.userName);
            Logger.info("As " + trimmedUser);
            Logger.info("reminders " + user.taskSet.size());
        }
        //return user;
    }

    public static void index() {
       // Company company = Company.find("select c from Company c where c.title='dimicro' ").first();
        AppUser user = AppUser.find("byUsername", Security.connected()).first();
        List<Project> projects = Project.find("select p from Project p where p.company =:comp").setParameter("comp", user.company).fetch();
        List<Person> persons = Person.findAll();
        List<Task> tasks = Task.find("select t from Task t where t.company =:comp and (t.endDate is null or t.endDate >=:custDate)")
                .setParameter("custDate", new DateTime().minusDays(5))
                .setParameter("comp", user.company).fetch();
        Logger.info("-----persons " + persons.size());

        render(tasks, projects, persons,user);
    }

    public static boolean userExists(String email) {
        AppUser user = AppUser.find("select au from AppUser au where UPPER(au.userName)=:username").setParameter("username", email).first();
        if (user != null)
            return true;
        else
            return false;
    }

    public static void doAddTask(String description) {
        AppUser user = AppUser.find("byUsername", Security.connected()).first();

        Task task = new Task();
        task.description = description;
        task.company = user.company;
        task.createdDate = new DateTime();
        task.status = "0";
        task.numbering = task.getMaxNumbering() + 1;
        task.uploader = user;
        task.save();
        index();
    }

    public static void showToDoList() {
        Logger.info("Showing todo list");
        AppUser user = AppUser.find("byUsername", Security.connected()).first();
        List<Task> tasks = null;
        Company company = user.company;
        boolean compactView = false;
        System.out.println(company.title);
        List<Project> projects = Project.find("select p from Project p where p.company =:comp").setParameter("comp", company).fetch();
        List<AppUser> pusers = AppUser.find("select u from AppUser u where u.company =:comp")
                .setParameter("comp", company).fetch();
        String uuid = UUID.randomUUID().toString();

        if(user.roleInfo.roleName.equals("viewer"))
        {
            //bring either the ones that are assigned to the viewer OR are assigned to a project that he has access to..
            tasks = Task.find("select t from Task t where t.company=:comp and t.status='0' and (t.endDate is null or t.endDate >=:custDate) and dtype='Task' and  (:value MEMBER OF t.userSet OR t.project.id in (select s.project.id from Share s where s.project is not null and s.user=:value ))")
                    .setParameter("comp", user.company)
                    .setParameter("custDate", new DateTime().minusDays(5))
                    .setParameter("value", user).fetch();

        }
        else {
            tasks = Task.find("select t from Task t where t.company =:comp and dtype='Task' and (t.endDate is null or t.endDate >=:custDate) order by t.createdDate asc")
                    .setParameter("custDate", new DateTime().minusDays(5))
                    .setParameter("comp", company).fetch();
        }
        Logger.info("how many tasks : "+tasks.size());

        String sql = "SELECT COUNT(t) FROM Task t where t.company =:comp and  dtype='Task' ";
        Query query = JPA.em().createQuery(sql);
        query.setParameter("comp", company);
        long lallTasks = (long) query.getSingleResult();
        Logger.info("-----projects " + projects.size());
        String allTasks = String.valueOf(lallTasks);
        Logger.info("posoi users " +pusers.get(0).getFullName());

        Logger.info("o USER " +user.getFullName());

        render(tasks, projects, pusers, allTasks, company, user,compactView, uuid);
    }


    public static void updateTask(Long projectId, Long taskId, Long userId, boolean status, String moveDirection) {
        Logger.info("------userId " + userId);
        Logger.info("------taskId " + taskId);
        Logger.info("------projectId " + projectId);
        AppUser currentUsr = AppUser.find("byUsername", Security.connected()).first();
        Task task = Task.findById(taskId);
        Project project = null;
        AppUser user = null;
        if (userId != null) {
            user = AppUser.findById(userId);
        }
        if (projectId != null) {
            project = Project.findById(projectId);
        }
        if (status) {
            task.status = "1";
            //save who marked the Task completed and the date
            task.endedBy = currentUsr;
            task.endDate = new DateTime();
        } else {
            task.status = "0";
        }

        if(project != null) {
            //opote an xanapathsei panw sto ergo na to kanei desselect apo to task
            if (project != null && task.project != null) {
                task.project = null;
                project.tasks.remove(task);
            } else {
                task.project = project;
                project.tasks.add(task);
            }
            project.save();
        }

        if (user != null) {
            if (task.userSet.contains(user)) {
                task.userSet.remove(user);
                user.taskSet.remove(task);
            } else {
                task.userSet.add(user);
                user.taskSet.add(task);
            }
            user.save();
        }


        if (moveDirection != null) {
            //TODO--------------
        }

        task.save();
        renderJSON(1);

    }

    public static void doAddReminder(Long reminderId, Long taskId, String[] perIds, String reminderDateString, Integer reminderMean) {
        if (taskId == null) {
            renderJSON(-1);
        }

        Reminder rem = null;
        Project proj = null;
        AppUser user = null;

        Task task = Task.findById(taskId);


        if (reminderId != null) {
            rem = Reminder.findById(reminderId);
        } else {
            rem = new Reminder();
        }

        rem.task = task;
        rem.reminderDate = DateTime.parse(reminderDateString, DateTimeFormat.forPattern("dd-MM-yyyy HH:mm"));
        rem.mean = Reminder.Means.values()[reminderMean];
        rem.save();
        if (perIds != null && perIds.length != 0) {
            for (int i = 0; i < perIds.length; i++) {
                if (perIds[i] != null) {
                    user = AppUser.findById(Long.valueOf(perIds[i]));
                    user.reminders.add(rem);
                    user.save();
                }
            }
        }


        renderJSON(1);

    }

    public static void deleteTask(Long id) {
        Logger.info("----- start  of task delete");
        Task task = Task.findById(id);

        for(AppUser u : task.userSet)
        {
            u.taskSet.remove(task);
            u.save();
        }
        List<Reminder> rems = Reminder.find("select r from Reminder r where r.task is ?1", task).fetch();
        for(Reminder r: rems)
        {
            Application.removeReminder(r.id);
        }
        Logger.info("----- end of task delete");
        task.delete();
    }

    public static void listTasksReminders(Long taskId) {
        Logger.info("task id -- " + taskId);
        Task task = Task.findById(taskId);
        List<Reminder> reminders = Reminder.find("select l from Reminder l where l.task =:ts").setParameter("ts", task).fetch();

        Logger.info("reminders size -- " + reminders.size());
        renderTemplate("app/views/Application/listTasksReminders.html", reminders, taskId);
    }

    public static void removeReminder(Long reminderId) {
        Reminder rem = Reminder.findById(reminderId);
        for(AppUser u : rem.userSet)
        {
            u.reminders.remove(rem);
            u.save();
        }
        rem.delete();
    }


    public static void listDocuments(Long customerId, Long projectId, Long leadId, Long userId) {
        boolean fromCustomer = false;
        Query query = null;
        AppUser user = AppUser.find("byUsername", Security.connected()).first();
        List<Document> docs = null;
        Project project = null;
        Lead lead = null;
        List<AppUser> users = null;
        Customer customer = null;
        Logger.info("-----@2@@@@@@@@@@@@@@@@------ " + customerId);
        if (customerId != null) {
            customer = Customer.findById(customerId);
            query = JPA.em().createQuery("select d from Document d where d.contact =:customer OR d.project in (select p.id from Project p where p.customer =:cust) and d.graphic = FALSE");
            query.setParameter("cust", customer);
            query.setParameter("customer", customer);
            docs = query.getResultList();
            fromCustomer = true;
        } else if (projectId != null) {
            project = Project.findById(projectId);
            if(user.roleInfo.roleName.equals("viewer"))
            {
                if(project.deliverables.size() > 0) {
                    query = JPA.em().createQuery("select d from Document d where (d.project =:proj or d.deliverable in :values)  and d.id IN (select s.document.id from Share s where s.document is not null and s.user.id =:usrID) and d.graphic = FALSE");
                    query.setParameter("usrID", user.id);
                    query.setParameter("proj", project);
                    query.setParameter("values", project.deliverables);

                }
                else{
                    query = JPA.em().createQuery("select d from Document d where d.project =:proj  and d.id IN (select s.document.id from Share s where s.document is not null and s.user.id =:usrID) and d.graphic = FALSE");
                    query.setParameter("usrID", user.id);
                    query.setParameter("proj", project);
                }
            }
            else{
                if(project.deliverables.size() > 0) {
                    query = JPA.em().createQuery("select d from Document d where (d.project =:proj or d.deliverable in :values) and d.graphic = FALSE");
                    query.setParameter("proj", project);
                    query.setParameter("values", project.deliverables);
                }
                else{
                    query = JPA.em().createQuery("select d from Document d where d.project =:proj and d.graphic = FALSE");
                    query.setParameter("proj", project);
                }
                docs = query.getResultList();
            }
            docs = query.getResultList();
        } else if (leadId != null) {
            lead = Lead.findById(leadId);
            query = JPA.em().createQuery("select d from Document d where d.lead =:ld and d.graphic = FALSE");
            query.setParameter("ld", lead);
            docs = query.getResultList();
        } else if(userId != null)
        {
            Logger.info("-----1user---");
           // AppUser  = AppUser.findById(userId);
            query = JPA.em().createQuery("select d from Document d where (d.id IN (select s.document.id from Share s where s.document is not null and s.user.id =:usrID) OR d.uploader.id=:usr) and d.graphic = FALSE");
            query.setParameter("usrID", userId);
            query.setParameter("usr", userId);
            docs = query.getResultList();
            Logger.info("-----2---" +docs.size());
        }

        query = JPA.em().createQuery("select u from AppUser u where u.company=:comp");
        query.setParameter("comp", user.company);
        users = query.getResultList();

        renderTemplate("app/views/projects/listDocuments.html", project, customer, users, docs, lead, fromCustomer,user);
    }

    public static void calendar() {
        render();
    }


    public static AppUser createAppUserFromPerson(Long personId) {
        Person person = Person.findById(personId);

        AppUser user = new AppUser();
        user.userName = person.contactInfo.email;
        user.password = BCrypt.hashpw("90fBB97a", BCrypt.gensalt(4));
        user.save();
        return user;

    }

    public static void listShares(Long documentId) {
        Query query = null;
        List<Person> persons = null;
        Document doc = Document.findById(documentId);
        List<Share> shares = Share.find("select s from Share s where s.document =:doc").setParameter("doc", doc).fetch();

        Logger.info("!!!!!!!!!!!!!!!!!!!!!!11----- " + shares.size());
        renderTemplate("app/views/Application/listShares.html", shares, doc);
    }

    public static void listSharesDoc(Long documentId) {
        Query query = null;
        List<Person> persons = null;
        Document doc = Document.findById(documentId);
        List<Share> shares = Share.find("select s from Share s where s.document =:doc").setParameter("doc", doc).fetch();

        Logger.info("!!!!!!!!!!!!!!!!!!!!!!11----- " + shares.size());
        renderTemplate("app/views/Application/listSharesDoc.html", shares, doc);
    }



    public static void removeShare(Long shareId) {
        Share share = Share.findById(shareId);
        AppUser user = AppUser.findById(share.user.id);
        if (user.shares.contains(share)) {
            user.shares.remove(share);
            user.save();
        }
        share.delete();

    }

    public static void doAddShare(Long shareId, Long docId, Long userId, String canView, String canComment) {
        Share sh = null;
        Document doc = Document.findById(docId);
        AppUser user = AppUser.findById(userId);

        if (shareId == null) {
            Logger.info("------if is null ----- " + shareId);
            Share test = Share.find("select s from Share s where user =:usr and document =:doc")
                    .setParameter("usr", user)
                    .setParameter("doc", doc).first();
            if (test != null) {
                renderJSON(-1);
            }
            sh = new Share();
            sh.document = doc;
            sh.user = user;
            if (canView != null && canView.equals("true")) {
                sh.canView = true;
            }
            if (canComment != null && canComment.equals("true")) {
                sh.canComment = true;
            }
            sh.save();
            user.shares.add(sh);
            user.save();
            doc.share.add(user);
            doc.save();
            if (doc.project == null)
                shareWithProject(user.id, doc.deliverable.project.id);
            else
                shareWithProject(user.id, doc.project.id);

        } else {
            sh = Share.findById(shareId);
            sh.document = doc;
            sh.user = user;
            if (canView != null && canView.equals("true")) {
                sh.canView = true;
            } else {
                sh.canView = false;
            }
            if (canComment != null && canComment.equals("true")) {
                sh.canComment = true;
            } else {
                sh.canComment = false;
            }
            sh.save();
            if (doc.project != null) {
                Project proj = Project.findById(doc.project.id);
                proj.userSet.add(user);
                proj.save();
            } else if (doc.deliverable.project != null) {
                Project proj = Project.findById(doc.deliverable.project.id);
                proj.userSet.add(user);
                proj.save();
            }
        }

        //TODO send the informative email ????? or NOT


        renderJSON(1);
    }

    public static void getShareInfo(Long shareId) {
        Logger.info("shareId" + shareId);
        try {
            Share share = Share.findById(shareId);
            JSONSerializer serializer;
            serializer = new JSONSerializer()
                    .include("*.id.*")
                    .include("*.canView.*")
                    .include("*.canComment.*")
                    .include("*.user.id.*")
                    .include("*.project.id.*")
                    .exclude("*");

            renderJSON(serializer.serialize(share));


        } catch (Exception ex) {
            Logger.info("ERROR getShareInfo " + ex);
            renderJSON(0);
        }
    }

    public static void shareWithProject(Long userId, Long projectId) {
        AppUser user = AppUser.findById(userId);
        Project project = Project.findById(projectId);

        Share chk = Share.find("select s from Share s where user =:usr and project =:proj")
                .setParameter("usr", user)
                .setParameter("proj", project).first();
        if (chk == null) {
            chk = new Share();
            chk.canComment = true;
            chk.canView = true;
            chk.project = project;
            chk.user = user;
            chk.save();
        }
    }

    public static void getEvents() {
        AppUser user = AppUser.find("byUsername", Security.connected()).first();
        List<Event.JSONEvent> eventList = new ArrayList<Event.JSONEvent>();
        List<Document> documents = null;
        List<Task> tasks = null;
        List<Project> projects = null;
        List<Lead> leads = null;
        List<Reminder> reminders = null;


        DateTime now = new DateTime();
        DateTime startOfYear = new DateTime(now.getYear(), 1, 1, 0, 0);
        Query query;

        query = JPA.em().createQuery("select t from Task t where t.company =:comp and t.createdDate > :startOfYear");
        query.setParameter("startOfYear", startOfYear);
        query.setParameter("comp", user.company);
        tasks = query.getResultList();
        for (Task task : tasks) {
            eventList.add(task.getEvent());
        }

        query = JPA.em().createQuery("select p from Project p where p.company =:comp and p.createDate > :startOfYear");
        query.setParameter("startOfYear", startOfYear);
        query.setParameter("comp", user.company);
        projects = query.getResultList();
        for (Project proj : projects) {
            eventList.add(proj.getEvent());
        }

        query = JPA.em().createQuery("select l from Lead l where l.company =:comp and l.createDate > :startOfYear");
        query.setParameter("startOfYear", startOfYear);
        query.setParameter("comp", user.company);
        leads = query.getResultList();
        for (Lead l : leads) {
            eventList.add(l.getEvent());
        }

        query = JPA.em().createQuery("select r from Reminder r where r.task in (select t from Task t where t.company =:comp) and r.reminderDate > :startOfYear");
        query.setParameter("startOfYear", startOfYear);
        query.setParameter("comp", user.company);
        reminders = query.getResultList();
        for (Reminder  r : reminders) {
            eventList.add(r.getEvent());
        }



        Logger.info("how many events --- " +eventList.size());
        renderJSON(eventList);

    }

    public static void navbarSearch(String query) throws NoSuchMethodException {
        Search search = new Search();
        if (query == null || query.equals("")) {
            renderJSON(JSONModel.getFromModels(search.doSearch("")));
        } else {
            renderJSON(JSONModel.getFromModels(search.doSearch(query)));
        }
    }

    public static void getCrossUserMessages(Integer seconds) {
        //renderJSON(null);
        renderJSON(Utils.getJSONMessageEvents(Utils.getLatest(seconds)));
    }
    public static void getUserTaskCount() {
        AppUser user = AppUser.find("byUsername", Security.connected()).first();
        List<Task> tasks = Task.find("select t from Task t where :value MEMBER OF t.userSet and t.status='0' ").setParameter("value", user).fetch();
        renderJSON(tasks.size());
    }
}

