package com.dowerx.quack.service;

import android.app.Activity;
import android.content.Context;
import android.os.AsyncTask;
import android.util.Log;
import android.widget.Toast;

import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.loader.content.AsyncTaskLoader;

import com.dowerx.quack.R;
import com.dowerx.quack.model.Post;
import com.dowerx.quack.model.User;
import com.google.android.gms.tasks.Tasks;
import com.google.firebase.auth.FirebaseAuth;
import com.google.firebase.firestore.DocumentReference;
import com.google.firebase.firestore.DocumentSnapshot;
import com.google.firebase.firestore.Filter;
import com.google.firebase.firestore.FirebaseFirestore;
import com.google.firebase.firestore.Query;
import com.google.firebase.firestore.QueryDocumentSnapshot;
import com.google.firebase.firestore.QuerySnapshot;

import java.util.Date;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;

public class PostService {
    public static class GetFeed extends AsyncTaskLoader<List<Post>> {

        public GetFeed(@NonNull Context context) {
            super(context);
        }

        @Nullable
        @Override
        public List<Post> loadInBackground() {
            List<Post> posts = new LinkedList<>();

            FirebaseFirestore db = FirebaseFirestore.getInstance();
            try {
                for (QueryDocumentSnapshot post : Tasks.await(db.collection("post").orderBy("date", Query.Direction.DESCENDING).get())) {
                    DocumentSnapshot user = Tasks.await(post.getDocumentReference("user").get());
                    String username = user.get("username", String.class);
                    String email = user.get("email", String.class);
                    String id = user.get("id", String.class);

                    QuerySnapshot likes = Tasks.await(
                            db.collection("like")
                                .where(Filter.equalTo("post", post.getReference()))
                                .get());

                    boolean liked = false;
                    for (DocumentSnapshot like : likes) {
                        if (Tasks.await(like.getDocumentReference("user").get()).getString("id").equals(AuthService.getId()))
                            liked = true;
                    }

                    posts.add(new Post(
                            new User(username, email, id),
                            post.get("content", String.class),
                            post.get("image", String.class),
                            likes.size(),
                            liked,
                            post.getId()
                    ));
                }
            } catch (ExecutionException | InterruptedException e) {
                throw new RuntimeException(e);
            }
            return posts;
        }

        @Override
        protected void onStartLoading() {
            forceLoad();
        }
    }

    public static class GetProfileFeed extends AsyncTaskLoader<List<Post>> {

        private final String username;

        public GetProfileFeed(@NonNull Context context, String username) {
            super(context);
            this.username = username;
        }

        @Nullable
        @Override
        public List<Post> loadInBackground() {
            List<Post> posts = new LinkedList<>();

            FirebaseFirestore db = FirebaseFirestore.getInstance();
            try {
                for (QueryDocumentSnapshot post : Tasks.await(db.collection("post")
                        .where(Filter.equalTo("user", db.document("/user/"+username)))
                        .orderBy("date", Query.Direction.DESCENDING)
                        .get())) {
                    DocumentSnapshot user = Tasks.await(post.getDocumentReference("user").get());
                    String username = user.get("username", String.class);
                    String email = user.get("email", String.class);
                    String id = user.get("id", String.class);

                    QuerySnapshot likes = Tasks.await(
                            db.collection("like")
                                    .where(Filter.equalTo("post", post.getReference()))
                                    .get());

                    boolean liked = false;
                    for (DocumentSnapshot like : likes) {
                        if (Tasks.await(like.getDocumentReference("user").get()).getString("id").equals(AuthService.getId()))
                            liked = true;
                    }

                    posts.add(new Post(
                            new User(username, email, id),
                            post.get("content", String.class),
                            post.get("image", String.class),
                            likes.size(),
                            liked,
                            post.getId()
                    ));
                }
            } catch (ExecutionException | InterruptedException e) {
                throw new RuntimeException(e);
            }

            return posts;
        }

        @Override
        protected void onStartLoading() {
            forceLoad();
        }
    }

    public static class NewPost extends AsyncTask<String, Void, Boolean> {

        Activity activity;

        public NewPost(Activity activity) {
            this.activity = activity;
        }

        @Override
        protected Boolean doInBackground(String... strings) {
            FirebaseFirestore db = FirebaseFirestore.getInstance();
            FirebaseAuth fba = FirebaseAuth.getInstance();

            Log.d(this.getClass().getName(), "before adding post");

            try {
                DocumentSnapshot user = Tasks.await(db.collection("user").where(Filter.equalTo("id", fba.getUid())).get()).getDocuments().get(0);
                HashMap<String, Object> post = new HashMap<>();
                post.put("content", strings[0]);
                post.put("image", "");
                post.put("user", user.getReference());
                post.put("date", new Date().getTime());
                Tasks.await(db.collection("post").add(post));

                Log.d(this.getClass().getName(), "after adding post");

            } catch (ExecutionException | InterruptedException e) {
                return false;
            }
            return true;
        }

        @Override
        protected void onPostExecute(Boolean result) {
            if (result) {
                activity.findViewById(R.id.nav_profile).performClick();
            } else {
                Toast toast = new Toast(activity);
                toast.setDuration(Toast.LENGTH_LONG);
                toast.setText("failed to send post");
                toast.show();
            }
        }
    }

    public static class RemovePost extends AsyncTask<String, Void, Boolean> {

        Callable<Void> callback;
        Context context;

        public RemovePost(Callable<Void> callback, Context context) {
            this.callback = callback;

            this.context = context;
        }

        @Override
        protected Boolean doInBackground(String... strings) {
            FirebaseFirestore db = FirebaseFirestore.getInstance();
            try {
                Tasks.await(db.document("/post/"+strings[0]).delete());
            } catch (ExecutionException | InterruptedException e) {
                return false;
            }
            return true;
        }

        @Override
        protected void onPostExecute(Boolean result) {
            if (result) {
                try {
                    this.callback.call();
                } catch (Exception e) {
                    throw new RuntimeException(e);
                }
            } else {
                Toast toast = new Toast(context);
                toast.setDuration(Toast.LENGTH_LONG);
                toast.setText("failed to remove post");
                toast.show();
            }
        }
    }

    // post_id, user_id
    public static class ToggleLike extends AsyncTask<String, Void, Integer> {

        private static final Integer LIKED = 0;
        private static final Integer DISLIKED = 1;
        private static final Integer ERROR = -1;

        private final Callable<Void> callback_liked;
        private final Callable<Void> callback_disliked;

        private final Context context;

        public ToggleLike(Callable<Void> callback_liked, Callable<Void> callback_disliked, Context context) {
            this.callback_liked = callback_liked;
            this.callback_disliked = callback_disliked;
            this.context = context;
        }

        @Override
        protected Integer doInBackground(String... strings) {
            FirebaseFirestore db = FirebaseFirestore.getInstance();

            try {
                DocumentReference post = db.document("/post/" + strings[0]);
                DocumentReference user = Tasks.await(
                    db.collection("user")
                        .where(Filter.equalTo("id", strings[1]))
                        .get())
                        .getDocuments().get(0).getReference();

                QuerySnapshot result = Tasks.await(
                    db.collection("like")
                        .where(Filter.equalTo("post", post))
                        .where(Filter.equalTo("user", user))
                        .get());

                // not liked yet, like it
                if (result.isEmpty())  {
                    HashMap<String, Object> data = new HashMap<>();
                    data.put("post", post);
                    data.put("user", user);
                    Tasks.await(db.collection("like").add(data));
                    return LIKED;
                }
                // already liked, remove the like
                else {
                    for (DocumentSnapshot doc : result) {
                        Tasks.await(doc.getReference().delete());
                    }
                    return DISLIKED;
                }
            } catch (ExecutionException | InterruptedException e) {
                return ERROR;
            }
        }

        @Override
        protected void onPostExecute(Integer result) {
            try {
                if (result == LIKED)
                    this.callback_liked.call();
                else if (result == DISLIKED)
                    this.callback_disliked.call();
                else {
                    Log.e(this.getClass().getName(), "failed to toggle like");
                    Toast toast = new Toast(context);
                    toast.setText("failed to toggle likes");
                    toast.setDuration(Toast.LENGTH_LONG);
                    toast.show();
                }
            } catch (Exception e) {
                Log.e(this.getClass().getName(), "failed in callbacks while attempting to toggle like");
                Toast toast = new Toast(context);
                toast.setText("failed to toggle likes");
                toast.setDuration(Toast.LENGTH_LONG);
                toast.show();
            }

        }
    }
}
