package controllers;

import flexjson.JSONSerializer;
import models.*;
import models.contacts.Contact;
import models.contacts.Customer;
import models.contacts.FinancialInfo;
import models.contacts.Person;
import models.projects.Deliverable;
import models.projects.Lead;
import models.projects.Project;
import models.projects.ProjectFinancialInfo;
import org.joda.time.DateTime;
import org.joda.time.format.DateTimeFormat;
import org.joda.time.format.DateTimeFormatter;
import play.Logger;
import play.data.validation.MaxSize;
import play.data.validation.Validation;
import play.db.jpa.GenericModel;
import play.db.jpa.JPA;
import play.mvc.Controller;

import javax.persistence.Query;
import javax.print.Doc;
import java.io.File;
import java.math.BigDecimal;
import java.net.MalformedURLException;
import java.util.*;
import java.util.stream.Collectors;

/**
 * Created by dom on 13/12/2018.
 */
public class Projects extends Application {

    static final DateTimeFormatter DTFIN = DateTimeFormat.forPattern("dd-MM-yyyy");


    public static void doUpload(File[] files, Long project_id, Long lead_id, Long customer_id, String title, boolean isImage, Long deliverable_id) throws MalformedURLException {
        AppUser usr = AppUser.find("byUsername", Security.connected()).first();
        Logger.info(Arrays.toString(files));

        Logger.info("lead_id --- " + lead_id);
        Logger.info("customer_id --- " + customer_id);
        Logger.info("project_id --- " + project_id);
        Logger.info("deliverable_id --- " + deliverable_id);

        for (File file : files) {
            Document doc = new Document();
            DBFile dbfile = new DBFile(file, file.getName(), title).save();
            if (isImage) {
                doc.graphic = true;
            }
            doc.uploadDate = DateTime.now();
            doc.dbfile = dbfile;
            doc.uploader = usr;
            doc.title = file.getName();
            if (deliverable_id != null && deliverable_id != 0) {
                Deliverable del = Deliverable.findById(deliverable_id);
                del.attachments.add(doc);
                doc.deliverable = del;
                doc.save();
                del.save();

            } else if (project_id != null) {
                Project project = Project.findById(project_id);
                project.fileList.add(dbfile);
                doc.project = project;
                doc.save();
                project.save();

            } else if (lead_id != null) {
                Lead lead = Lead.findById(lead_id);
                doc.lead = lead;
                doc.save();
                lead.attachments.add(doc);
                lead.save();
            } else if (customer_id != null) {
                Contact con = Contact.findById(customer_id);
                con.documents.add(doc);
                doc.contact = con;
                doc.save();
                con.save();
            }

        }

        if (project_id != null) {
            addProject(project_id);
        } else if (lead_id != null) {
            addLead(lead_id);
        } else if (customer_id != null) {
            Contacts.addContact(customer_id);
        }
    }

    public static void filterProjects(Long contactID) {
        AppUser _USER = AppUser.find("byUsername", Security.connected()).first();
        renderArgs.put("active", "project");
        renderArgs.put("pageHeader", "Έργα");
        renderArgs.put("pageHeaderDetail", "Λίστα έργων ");
        Logger.info("-------------------- " + _USER.userName);
        Contact contact = null;
        if(contactID != null)
        {
            contact = Contact.findById(contactID);

        }
        renderTemplate("app/views/projects/filterProjects.html", _USER, contact);
    }

    public static void listProjects(Long contactId) {
        AppUser usr = AppUser.find("byUsername", Security.connected()).first();
        List<Project> projects = null;
        List<Document> docs = null;
        Logger.info("---------mm----------- " + contactId);
        if (contactId == null) {
            if (usr.roleInfo.roleName.equals("viewer")) {
                Query query = JPA.em().createQuery("select p from Project p where p.company =:comp and p.id in (select s.project.id from Share s  where s.project is not null and s.user.id =:usrID) ");
                query.setParameter("usrID", usr.id);
                query.setParameter("comp", usr.company);
                projects = query.getResultList();
            } else {
                Query query = JPA.em().createQuery("select p from Project p where p.company =:comp ");
                query.setParameter("comp", usr.company);
                projects = query.getResultList();
            }
        } else {
            Contact contact = Contact.findById(contactId);
            if (contact.getClass().getSimpleName().equals("Customer")) {
                Logger.info("CUSTOMER---");
                Customer cust = Customer.findById(contactId);
                projects = cust.projects;
            } else {
                AppUser user = AppUser.findById(contactId);
                projects = user.getPersonProjects();
            }
        }
        Logger.info("---person project size-- " + projects.size());
        renderTemplate("app/views/projects/listProjects.html", projects);

    }

    public static void filterLeads() {
        renderArgs.put("active", "lead");
        renderArgs.put("pageHeader", "Leads");
        renderArgs.put("pageHeaderDetail", "Λίστα συζητήσεων που μπορεί να μετατραπούν σε έργα ");
        Company company = Company.find("select c from Company c where c.title='dimicro' ").first();
        List<Customer> customers = Customer.find("byCompany", company).fetch();

        renderTemplate("app/views/projects/filterLeads.html", customers);
    }

    public static void listLeads() {
        AppUser usr = AppUser.find("byUsername", Security.connected()).first();
        List<Lead> leads = Lead.find("select l from Lead l where l.company =:comp").setParameter("comp", usr.company).fetch();
        renderTemplate("app/views/projects/listLeads.html", leads);

    }

    public static void addProject(Long id) {
        AppUser _USER = AppUser.find("byUsername", Security.connected()).first();
        renderArgs.put("active", "projects");
        renderArgs.put("pageHeader", "Καρτέλα Έργου");
        renderArgs.put("pageHeaderDetail", "Φόρμα εισαγωγής νέου έργου");
        AppUser usr = new AppUser();
        Project project = null;
        Contact contact = null;
        List<Document> documents = null;
        List<Deliverable> dels = null;
        List<Customer> customers = Customer.find("select c from Contact c where dtype='Customer' and c.company =:comp ").setParameter("comp", _USER.company).fetch();
        if (id != null) {
            project = Project.findById(id);
            documents = Document.find("select d from Document d where d.project = :proj").setParameter("proj", project).fetch();
            dels = Deliverable.find("select d from Deliverable d where d.project =:proj").setParameter("proj", project).fetch();

        } else {

        }

        renderTemplate("app/views/projects/addProject.html", project, customers, dels, _USER);
    }

    public static void addLead(Long id) {
        renderArgs.put("active", "leads");
        renderArgs.put("pageHeader", "Καρτέλα lead");
        renderArgs.put("pageHeaderDetail", "Φόρμα εισαγωγής νέας προοπτικης");
        AppUser user = AppUser.find("byUsername", Security.connected()).first();
        Contact contact = null;
        Lead lead = null;
        List<Document> documents = null;
        List<Customer> customers = Customer.find("select c from Contact c where dtype='Customer' and c.company =:comp ").setParameter("comp", user.company).fetch();
        List<Person> persons = Person.find("select p from Person p where p.personType =:pertype and p.company =:comp").setParameter("comp", user.company).setParameter("pertype", Person.PersonType.ASSOCIATE).fetch();
        if (id != null) {
            lead = Lead.findById(id);
            documents = Document.find("select d from Document d where d.lead = :lead").setParameter("lead", lead).fetch();

        } else {

        }

        renderTemplate("app/views/projects/addLead.html", lead, customers, persons);
    }

    public static void doAddProject(Long id, @MaxSize(value = 50, message = "Όχι πάνω απο 50 χαρακτήρες") String name, @MaxSize(value = 1150, message = "Όχι πάνω απο 150 χαρακτήρες") String description
            , @MaxSize(value = 2, message = "Όχι πάνω απο 150 χαρακτήρες") String status, Long customerId, String projectStartDateString, String projectDeliverDateString, BigDecimal cost, BigDecimal downpayment) {

        validation.required(name);

        if (Validation.hasErrors()) {
            Logger.info(validation.errorsMap().toString());
            params.flash();
            Validation.keep();
            addProject(id);
        }

        AppUser user = AppUser.find("byUsername", Security.connected()).first();
        Project project = null;

        Customer customer = Customer.findById(customerId);

        DateTime projectStarDate = null;
        if (!projectStartDateString.equals("")) {
            try {
                projectStarDate = DTFIN.parseDateTime(projectStartDateString);
            } catch (Exception e) {
                Logger.error("invalid date ");
            }
        }

        DateTime projectDeliverDate = null;
        if (!projectDeliverDateString.equals("")) {
            try {
                projectDeliverDate = DTFIN.parseDateTime(projectDeliverDateString);
            } catch (Exception e) {
                Logger.error("invalid date ");
            }
        }

        if (id == null) {
            project = new Project();
            project.name = name;
            project.company = user.company;
            project.description = description;
            project.customer = customer;
            project.status = status;
            project.projectStartDate = projectStarDate;
            project.projectDeliverDate = projectDeliverDate;
            project.financialInfo = new ProjectFinancialInfo(cost, downpayment, "", "");
            project.createDate = new DateTime();
            project.uploader = user;

        } else {
            project = Project.findById(id);
            project.name = name;
            project.company =  user.company;
            project.description = description;
            project.customer = customer;
            project.status = status;
            project.projectStartDate = projectStarDate;
            project.projectDeliverDate = projectDeliverDate;
            project.financialInfo = new ProjectFinancialInfo(cost, downpayment, "", "");
            project.uploader = user;
        }

        project.save();
        flash.put("success", "");
        addProject(project.id);

    }

    public static void doAddLead(Long id, @MaxSize(value = 50, message = "Όχι πάνω απο 50 χαρακτήρες") String name, String description
            , String statusId, Long personId, Long customerId) {


        validation.required(name);
        if (Validation.hasErrors()) {
            Logger.info(validation.errorsMap().toString());
            params.flash();
            Validation.keep();
            addLead(id);
        }

        AppUser user = AppUser.find("byUsername", Security.connected()).first();
        Person p = Person.findById(personId);
        Customer cust = Customer.findById(customerId);


        Lead l = null;

        if (id == null) {
            l = new Lead();
            l.name = name;
            l.description = description;
            l.handledBy = p;
            l.customer = cust;
            l.status = statusId;
            l.company = user.company;
            l.createDate = new DateTime();
            l.uploader = user;
        } else {
            l = Lead.findById(id);
            l.name = name;
            l.description = description;
            l.handledBy = p;
            l.customer = cust;
            l.status = statusId;
            l.company = user.company;
            l.createDate = new DateTime();
            l.uploader = user;
        }

        l.save();
        flash.put("success", "");
        addLead(l.id);


    }

    public static void doAddDeliverable(Long projectId, Long deliverableId, String description, String deliverableInfo, String endDateStr, String status) {
        AppUser user = AppUser.find("byUsername", Security.connected()).first();
        Logger.info("--------projectId " + projectId);
        Logger.info("--------description " + description);
        Logger.info("--------deliveredDateStr " + endDateStr);
        Project proj = Project.findById(projectId);
        Deliverable del = null;

        if (description.equals("") || deliverableInfo.equals("")) {
            renderJSON(-1);
        }
        DateTime endDate = null;
        if (!endDateStr.equals("")) {
            try {
                endDate = DTFIN.parseDateTime(endDateStr);
            } catch (Exception e) {
                Logger.error("invalid date in doAddDeliverable ");
            }
        }

        if (deliverableId == null) {
            del = new Deliverable();
            del.project = proj;
            del.description = description;
            del.deliverableInfo = deliverableInfo;
            del.status = status;
            del.endDate = endDate;
            del.createdDate = new DateTime();
            del.uploader = user;
            del.company = user.company;
            del.save();
            proj.deliverables.add(del);
            proj.save();
        } else {
            del = Deliverable.findById(deliverableId);
            del.description = description;
            del.project = proj;
            del.deliverableInfo = deliverableInfo;
            del.status = status;
            del.endDate = endDate;
            del.uploader = user;
            del.save();
        }

        renderJSON(1);

    }

    public static void bindAssociateToProject(Long projectId, Long userId) throws Throwable {
        Project project = Project.findById(projectId);
        AppUser user = AppUser.findById(userId);
        Share tt = Share.find("select s from Share s where user =:usr and project =:proj")
                .setParameter("usr", user)
                .setParameter("proj", project).first();
        if (tt != null) {
            //first remove deliverable shares
            for (Deliverable del : project.deliverables) {
                Share s_del = Share.find("select s from Share s where user =:usr and deliverable =:del")
                        .setParameter("usr", user)
                        .setParameter("del", del).first();

                if (s_del != null) {
                    del.userSet.remove(user);
                    s_del.delete();
                    del.save();
                }
            }

            //remove sharing from any documents of the project
            if (project.fileList.size() != 0) {
                List<Document> docs = Document.find("select d from Document d where d.dbfile IN :values")
                        .setParameter("values", project.fileList).fetch();

                for (Document doc : docs) {
                    Share dd = Share.find("select s from Share s where  user =:usr and document =:doc")
                            .setParameter("usr", user)
                            .setParameter("doc", doc).first();
                    if (dd != null) {
                        user.shares.remove(dd);
                        user.save();
                        dd.delete();
                    }
                }
            }


            //remove sharing from any document from the deliverables of the project
            for (Deliverable del : project.deliverables) {
                for (Document doc : del.attachments) {
                    Share cc = Share.find("select s from Share s where user =:usr and document =:doc")
                            .setParameter("usr", user)
                            .setParameter("doc", doc).first();

                    if (cc != null) {
                        user.shares.remove(cc);
                        user.save();
                        cc.delete();
                    }
                }
            }


            //finally remove the project share
            tt.delete();
            project.userSet.remove(user);
            project.save();

            EmailController.accessToProject(user.id, project.id, false);

            renderJSON(-1);
        } else {
            Share share = new Share();
            share.canComment = true;
            share.canView = true;
            share.project = project;
            share.user = user;
            share.save();
            project.userSet.add(user);
            project.save();

            //we see wether this person is actual an appUser if yes send him a mail informing him on his new access to a project
            //IF he is not an AppUser send him an invitation for registration
            EmailController.accessToProject(user.id, project.id, true);
//            if (person.getClass().getSimpleName().equals("AppUser")) {
//                EmailController.accessToProject(person.id, project.id, true);
//                renderJSON(1);
//            } else {
//                EmailController.sendInvite(person.id);
//                renderJSON(2);
//            }

        }


        //TODO they should be given username password so they can login to the platform and have access to projects info/documennts


    }

    public static void showProjectTasks(Long projectId)
    {
        AppUser user = AppUser.find("byUsername", Security.connected()).first();
        Project proj = Project.findById(projectId);
        List<Task> tasks = new ArrayList<>();
        tasks = proj.tasks;
        boolean compactView = true;

        tasks = proj.tasks.stream().filter(t -> t.status.equals("0")).collect(Collectors.toList());
        tasks =  tasks.stream().filter(t -> t.getClass().getSimpleName().equals("Task")).collect(Collectors.toList());

        Logger.info("how many task this project has -- " +proj.tasks.size());
        renderTemplate("app/views/Application/showToDoList.html", tasks, user, compactView);

    }

    public static void searchLeads(String status, String dateRangeString, Long customerId) {
        Logger.info("----status " + status);
        Logger.info("----dateRangeString " + dateRangeString);
        Logger.info("----customerId " + customerId);
        AppUser user = AppUser.find("byUsername", Security.connected()).first();
        Map<String, Object> paramMap = new HashMap<String, Object>();
        String queryString = "";
        List<Lead> leads = null;
        queryString = "select l from Lead l where l.company =:comp ";
        paramMap.put("comp", user.company);

        if (customerId != null && customerId != 0) {
            Customer cust = Customer.findById(customerId);
            queryString += " AND l.customer =:cust";
            paramMap.put("cust", cust);
        }
        if (!dateRangeString.equals("")) {

            String[] drange = dateRangeString.split(";");
            DateTime fromDate = new DateTime();
            DateTime toDate = new DateTime();
            DateTime tmp = new DateTime();

            try {
                fromDate = DTFIN.parseDateTime(drange[0]);
            } catch (Exception e) {
                Logger.error("ERROR dateParse searchLeads");
            }
            try {
                toDate = DTFIN.parseDateTime(drange[1]);
            } catch (Exception e) {
                Logger.error("ERROR dateParse searchLeads");
            }
            queryString += " AND l.createDate  BETWEEN :startDate AND :endDate ";
            paramMap.put("startDate", fromDate);
            paramMap.put("endDate", tmp);
        }
        if (!status.equals("0")) {
            queryString += " AND l.status =:stat";
            paramMap.put("stat", status);
        }

        javax.persistence.Query queryA = JPA.em().createQuery(queryString);
        for (String key : paramMap.keySet()) {
            queryA.setParameter(key, paramMap.get(key));
        }

        leads = queryA.getResultList();
        Logger.info("List leads end " + leads.size());
        renderTemplate("app/views/projects/listLeads.html", leads);
    }

    public static void listProjectDeliverables(Long projectId) {
        AppUser user = AppUser.find("byUsername", Security.connected()).first();
        Project proj = Project.findById(projectId);
        List<Deliverable> deliverables = null;
        Query query = null;
        List<AppUser> associates = AppUser.find("select u from AppUser u where u.company=:comp ")
                .setParameter("comp", user.company)
                .fetch();
        if (user.roleInfo.roleName.equals("viewer")) {
            query = JPA.em().createQuery("select d from Deliverable d where d.project = :proj and d.id in (select s.deliverable.id from Share s  where s.deliverable is not null and s.user.id =:usrID) ");
            query.setParameter("usrID", user.id);
        } else {
            query = JPA.em().createQuery("select d from Deliverable d where d.project = :proj ");
        }
        query.setParameter("proj", proj);
        deliverables = query.getResultList();

        Logger.info("posa deliverables : " + deliverables.size());
        renderTemplate("app/views/projects/listProjectDeliverables.html", deliverables, associates, user);

    }

    public static void deleteDeliverable(Long deliverableId) {
        Deliverable del = Deliverable.findById(deliverableId);
        Project proj = Project.findById(del.project.id);
        proj.deliverables.remove(del);
        proj.save();

        Logger.info("yolooooo " + del.attachments.size());
        //delete all of the deliverable's documents along with their shares
        Logger.info("::::::::: " + del.attachments);

        List<Document> toDelete = new ArrayList<>();
        for (Document doc : del.attachments) {
            Logger.info("inside doc loop " + doc.id);
            List<Share> docShares = Share.find("Select s from Share s where s.document =:doc")
                    .setParameter("doc", doc).fetch();
            Logger.info("inside " + docShares.size());

            for (Share sdoc : docShares) {
                Person p = Person.findById(sdoc.user.id);
                p.shares.remove(sdoc);
                p.save();
                sdoc.delete();
            }

            Logger.info("test1 ");
//            del.attachments.remove(doc);
//            doc.deliverable.delete();
            Logger.info("test12 ");
            doc.deliverable = null;

            doc.save();
            toDelete.add(doc);
            del.save();
            //   doc.delete();

            Logger.info("test13 ");
        }
        del.attachments.clear();
        del.save();
        toDelete.forEach(GenericModel::delete);

        //delete all the shares of the deliverable
        List<Share> shares = Share.find("Select s from Share s where s.deliverable =:del")
                .setParameter("del", del).fetch();
        for (Share s : shares) {
            AppUser u = AppUser.findById(s.user.id);
            u.shares.remove(s);
            u.save();
            s.delete();
        }

        Logger.info("::::::::: " + del.attachments);

        del.userSet.clear();

        del.delete();
        renderJSON(1);
    }

    public static void getDeliverableDetails(Long deliverableId) {

        try {
            Deliverable del = Deliverable.findById(deliverableId);
            JSONSerializer serializer;
            serializer = new JSONSerializer()
                    .include("*.id.*")
                    .include("*.description.*")
                    .include("*.deliverableInfo.*")
                    .include("*.endDate.*")
                    .include("*.status.*")

                    .exclude("*");

            renderJSON(serializer.serialize(del));


        } catch (Exception ex) {
            Logger.info("ERROR getDeliverableDetails " + ex);
            renderJSON(0);
        }
    }

    public static void updateDeliverablePersons(Long deliverableId, Long userId, String actionTyp) throws Throwable {
        Deliverable del = Deliverable.findById(deliverableId);
        AppUser user;
        Share share = null;
        Project proj = Project.findById(del.project.id);
        user = AppUser.findById(userId);
        if (actionTyp.equals("add")) {
            share = new Share();
            share.canView = true;
            share.canComment = true;
            share.deliverable = del;
            share.user = user;
            share.save();
            del.userSet.add(user);
            del.save();
            Application.shareWithProject(user.id, proj.id);
            EmailController.accessToDeliverable(user.id, del.id, true);
            renderJSON(-1);

        } else {
            share = Share.find("select s from Share s where user =:usr and deliverable =:del")
                    .setParameter("usr", user)
                    .setParameter("del", del).first();
            share.delete();
            del.userSet.remove(user);
            del.save();
            EmailController.accessToDeliverable(user.id, del.id, false);
            renderJSON(1);
        }


    }

    public static void listProjectImages(Long projectId) {
        Query query = null;
        AppUser usr = AppUser.find("byUsername", Security.connected()).first();
        List<Document> docs = null;
        List<AppUser> users = null;
        Project project = Project.findById(projectId);
        if (usr.roleInfo.roleName.equals("viewer")) {
            Logger.info("must be here ----- ");
            if (project.deliverables.size() > 0) {
                Logger.info("must be here alos----- " + project.deliverables.size());
                query = JPA.em().createQuery("select d from Document d where (d.project =:proj or d.deliverable in :dels) and d.id IN (select s.document.id from Share s where s.document is not null and s.user.id =:usrID and s.canView = TRUE) and d.graphic = TRUE");
                query.setParameter("usrID", usr.id);
                query.setParameter("dels", project.deliverables);
                Logger.info("to query----- " + query);
            } 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 s.canView = TRUE) and d.graphic = TRUE");
                query.setParameter("usrID", usr.id);
            }
        } else {
            if (project.deliverables.size() > 0) {
                query = JPA.em().createQuery("select d from Document d where (d.project =:proj or d.deliverable in :dels) and d.graphic = TRUE");
                query.setParameter("dels", project.deliverables);
            } else {
                query = JPA.em().createQuery("select d from Document d where d.project =:proj  and d.graphic = TRUE");
            }
        }
        query.setParameter("proj", project);

        docs = query.getResultList();
        query = JPA.em().createQuery("select p from AppUser p where p.company=:comp");
        query.setParameter("comp", usr.company);
        users = query.getResultList();

        Logger.info("perons files " + users.size());

        renderTemplate("app/views/projects/listProjectImages.html", docs, users, project, usr);
    }

    public static void deleteFile(Long fileId) {
        Document doc = Document.findById(fileId);
        DBFile dbFile = DBFile.findById(doc.dbfile.id);
        if (doc.project != null) {
            Project proj = Project.findById(doc.project.id);
            proj.fileList.remove(dbFile);
            proj.save();
        }
        if (doc.deliverable != null) {
            Deliverable del = Deliverable.findById(doc.deliverable.id);
            del.attachments.remove(doc);
            del.save();
        }
        if (doc.contact != null) {
            Contact con = Contact.findById(doc.contact.id);
            con.photo = null;
            con.save();
        }

        //remove any sharing of this document from Share and from Person.share
        List<Share> shares = Share.find("select s from Share s where document =:doc")
                .setParameter("doc", doc).fetch();

        for (Share sh : shares) {
            Person p = Person.findById(sh.user.id);
            p.shares.remove(sh);
            p.save();
            sh.delete();
        }
        doc.delete();
        dbFile.delete();

    }

    //since every sharing that is being done in any object that is part of a project (file,image,deliverable etc) there is always a registration of a
    // sharing the project we just need to get the projects hsares and we have everything
    public static void getProjectsShareDetail(Long projectID) {
tID);
        List<Share> shares = null;
        Project proj = Project.findById(projectID);
        shares = Share.find("select s from Share s where s.project =:proj")
                .setParameter("proj", proj).fetch();

        Logger.info("project shares aaaaaaaa" +shares.size());
        renderJSON(shares);
    }


}
