package com.p3.bewoplaner.page.chat;

import com.p3.bewoplaner.R;
import com.p3.bewoplaner.data.AppPrefsConstants;
import com.p3.bewoplaner.data.events.InvalidTokenEvent;
import com.p3.bewoplaner.data.events.LogoutDirectlyEvent;
import com.p3.bewoplaner.data.prefs.BooleanPreference;
import com.p3.bewoplaner.data.prefs.LongPreference;
import com.p3.bewoplaner.di.InjectionHelper;
import com.p3.bewoplaner.model.Group;
import com.p3.bewoplaner.model.Message;
import com.p3.bewoplaner.page.common.RuntimePermissionActivity;
import com.p3.bewoplaner.page.login.LoginPresenter;
import com.p3.bewoplaner.page.main.LogoutNavigation;
import com.p3.bewoplaner.utils.EndlessScrollListener;
import com.p3.bewoplaner.utils.FileUtils;
import com.p3.bewoplaner.utils.SystemUtils;
import com.p3.bewoplaner.utils.ui.EmptyLayout;

import org.greenrobot.eventbus.EventBus;
import org.greenrobot.eventbus.Subscribe;
import org.greenrobot.eventbus.ThreadMode;

import android.Manifest;
import android.app.Activity;
import android.content.Intent;
import android.graphics.Rect;
import android.net.Uri;
import android.os.Build;
import android.os.Bundle;
import android.os.Environment;
import android.os.PersistableBundle;
import android.support.annotation.Nullable;
import android.support.annotation.StringRes;
import android.support.design.widget.CoordinatorLayout;
import android.support.design.widget.Snackbar;
import android.support.v4.app.NotificationManagerCompat;
import android.support.v4.content.ContextCompat;
import android.support.v4.content.FileProvider;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.support.v7.widget.SimpleItemAnimator;
import android.text.Editable;
import android.text.TextUtils;
import android.util.Log;
import android.view.View;
import android.view.ViewTreeObserver;
import android.view.WindowManager;
import android.widget.ImageView;
import android.widget.TextView;
import android.widget.Toast;

import java.io.File;
import java.util.ArrayList;

import javax.inject.Inject;
import javax.inject.Named;

import butterknife.BindView;
import butterknife.ButterKnife;
import butterknife.OnClick;
import butterknife.OnTextChanged;
import hani.momanii.supernova_emoji_library.Actions.EmojIconActions;
import hani.momanii.supernova_emoji_library.Helper.EmojiconEditText;
import okhttp3.MediaType;

import static com.p3.bewoplaner.data.AppPrefsConstants.USER_ID;

/**
 * Created by Rares Teodorescu on 21/02/2017.
 */
abstract class BaseChatActivity extends RuntimePermissionActivity
        implements ChatView, BaseChatAdapter.MessageClickListener, LogoutNavigation {

    private final static int STORAGE_REQUEST_CODE = 9999;
    private final static int SELECT_FILE = 5055;
    private final static int VISIBLE_THRESHOLD = 25;
    protected final static String MESSAGE_ARG = "message_arg";
    protected final static String GROUP_ARG = "group_arg";

    @BindView(R.id.chat_toolbar_image_view)
    ImageView mChatImageView;

    @BindView(R.id.chat_toolbar_single_title)
    TextView mSingleChatTitleTextView;

    @BindView(R.id.chat_toolbar_group_title)
    TextView mGroupTitleTextView;

    @BindView(R.id.chat_toolbar_group_members)
    TextView mGroupMembersTextView;

    @BindView(R.id.chat_toolbar_extra_image)
    ImageView mExtraImageView;

    @BindView(R.id.chat_message)
    EmojiconEditText mChatMessageEmojiEditText;

    @BindView(R.id.chat_recycler_view)
    RecyclerView mRecyclerView;

    @BindView(R.id.chat_empty_layout)
    EmptyLayout mEmptyLayout;

    @BindView(R.id.emoji_button)
    ImageView mEmojiButton;

    @BindView(R.id.chat_send_message)
    ImageView mSendMessageImageView;

    @BindView(R.id.chat_coordinator_layout)
    CoordinatorLayout mCoordinatorLayout;

    @Inject
    @Named(USER_ID)
    LongPreference mUserIdPreference;

    @Inject
    ChatPresenter mChatPresenter;

    @Inject
    SystemUtils mSystemUtils;

    @Inject
    LoginPresenter mLoginPresenter;

    protected Group mGroup;
    protected ChatView.Callback mCallback;
    protected EndlessScrollListener mEndlessScrollListener;
    protected BaseChatAdapter mBaseChatAdapter;
    private Snackbar mSnackbar;

    @Inject
    @Named(AppPrefsConstants.CHAT_OPENED)
    BooleanPreference mChatOpenedPref;

    @Override
    public void onCreate(@Nullable Bundle savedInstanceState) {
        if (!getIntent().hasExtra(GROUP_ARG) && !getIntent().hasExtra(MESSAGE_ARG)) {
            throw new RuntimeException("Arguments are missing. Please use createIntentMethodInstead;");
        }
        super.onCreate(savedInstanceState);
        EventBus.getDefault().register(this);
        setContentView(R.layout.activity_chat);
        ButterKnife.bind(this);
        mSendMessageImageView.setEnabled(false);
        InjectionHelper.getChatComponent(this).inject(this);
        mBaseChatAdapter = getAdapter();
        mEmptyLayout.setOnTryAgainClickListener(v -> mCallback.fetchMoreMessagesByGroupId());
        setupRecyclerView();
        EmojIconActions emojIcon = new EmojIconActions(this, mCoordinatorLayout, mChatMessageEmojiEditText, mEmojiButton);
        emojIcon.ShowEmojIcon();
        showLoading();
        getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE);
        getWindow().getDecorView().getViewTreeObserver().addOnGlobalLayoutListener(mKeyboardListener);
    }

    private void setupRecyclerView() {
        LinearLayoutManager linearLayoutManager = new LinearLayoutManager(this);
        linearLayoutManager.setStackFromEnd(true);
        linearLayoutManager.setReverseLayout(true);
        mRecyclerView.setLayoutManager(linearLayoutManager);
        mBaseChatAdapter.setMessageClickListener(this);
        mRecyclerView.setAdapter(mBaseChatAdapter);
        ((SimpleItemAnimator) mRecyclerView.getItemAnimator()).setSupportsChangeAnimations(false);
        mEndlessScrollListener = new EndlessScrollListener(VISIBLE_THRESHOLD, linearLayoutManager) {
            @Override
            public boolean onLoad() {
                if (mCallback == null || mCallback.isLoading()) {
                    return false;
                }
                mCallback.fetchMoreMessagesByGroupId();
                return true;
            }
        };
        mRecyclerView.addOnScrollListener(mEndlessScrollListener);
    }

    @OnClick(R.id.chat_toolbar_extra_image)
    void extraImageViewClicked() {
        extraImageClicked();
    }

    protected abstract void extraImageClicked();

    protected abstract void setToolbar();

    protected abstract BaseChatAdapter getAdapter();

    protected abstract void loadMessages();

    @Override
    public void setCallback(Callback callback) {
        mCallback = callback;
    }

    @Override
    public void messagesLoaded(ArrayList<Message> messages, boolean isFirstPage) {
        if (isFirstPage) {
            mEndlessScrollListener.reset();
            mBaseChatAdapter.setMessages(messages);
            mRecyclerView.scrollToPosition(0);
            return;
        }
        mBaseChatAdapter.addMessages(messages);
    }

    @Override
    public void showError(@StringRes int errorRes) {
        if (mSnackbar == null) {
            mSnackbar = Snackbar.make(mCoordinatorLayout, errorRes, Snackbar.LENGTH_LONG);
            mSnackbar.setActionTextColor(ContextCompat.getColor(this, R.color.colorDefault));
        }
        mSnackbar.setText(errorRes);
        mSnackbar.show();
    }

    @Override
    public void showLoading() {
        mEmptyLayout.showLoading();
    }

    @Override
    public void hideLoading() {
        mEmptyLayout.setVisibility(View.GONE);
    }

    @Override
    public void showMessagesLoading() {
        mBaseChatAdapter.setLoading(true);
    }

    @Override
    public void hideMessagesLoading() {
        mBaseChatAdapter.setLoading(false);
    }

    @Override
    public void showEmptyMessage() {
        mEmptyLayout.setEmptyText(getString(R.string.chat_no_messages));
        mEmptyLayout.showEmpty();
    }

    @OnClick(R.id.chat_attachment)
    void attachmentClicked() {
        requestAppPermissions(new String[]{Manifest.permission.READ_EXTERNAL_STORAGE},
                R.string.permission_storage_explained,
                STORAGE_REQUEST_CODE,
                this::selectFile);
    }

    @OnClick(R.id.chat_toolbar_back_arrow)
    void backPressed() {
        onBackPressed();
    }

    @OnClick(R.id.chat_send_message)
    void messageSent() {
        mLoginPresenter.checkIfVoucherStillAvailable(this);
        mCallback.sendMessage(mChatMessageEmojiEditText.getText().toString());
        mChatMessageEmojiEditText.setText("");
    }

    @OnTextChanged(value = R.id.chat_message, callback = OnTextChanged.Callback.AFTER_TEXT_CHANGED)
    void messageTextChanged(Editable editable) {
        mSendMessageImageView.setEnabled(!TextUtils.isEmpty(editable.toString()) && editable.toString().trim().length() > 0);
    }

    private void selectFile(int requestCode) {
        Intent intent;
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
            intent = new Intent(Intent.ACTION_OPEN_DOCUMENT);
            intent.addFlags(Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION);
        } else {
            intent = new Intent(Intent.ACTION_GET_CONTENT);
        }
        intent.setType("*/*");
        intent.putExtra(Intent.EXTRA_LOCAL_ONLY, true);
        intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
        startActivityForResult(Intent.createChooser(intent, getResources().getString(R.string.chat_select_file)), SELECT_FILE);
    }

    @Override
    protected void onResume() {
        super.onResume();
        mLoginPresenter.checkIfVoucherStillAvailable(this);
    }

    @Override
    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
        super.onActivityResult(requestCode, resultCode, data);
        if (resultCode != Activity.RESULT_OK) {
            return;
        }
        if (null == data) {
            return;
        }
        if (requestCode == SELECT_FILE) {
            if (null == data.getData()) {
                return;
            }
            final Uri uri = data.getData();
            if (Build.VERSION.SDK_INT >= 19) {
                final int takeFlags = data.getFlags()
                        & (Intent.FLAG_GRANT_READ_URI_PERMISSION |
                        Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
                // Check for the freshest data.
                try {
                    getContentResolver().takePersistableUriPermission(uri, takeFlags);
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
            if (!mChatPresenter.isAttached()) {
                mChatPresenter.attachView(this);
            }
            mCallback.sendFile(uri);
        }
    }

    @Override
    public void logoutDirectly() {
        EventBus.getDefault().post(new LogoutDirectlyEvent());
        finish();
    }

    @Override
    public void showToastMessage(@StringRes int message) {
        Toast.makeText(BaseChatActivity.this, message, Toast.LENGTH_SHORT).show();
    }

    @Override
    public void fileClicked(Message message, int itemPosition) {
        if (!mChatPresenter.isAttached()) {
            mChatPresenter.attachView(this);
        }
        String[] split = message.filePath.split("/");
        String fileName = split[split.length - 1];
        File file = getExternalFilesDir(null);
        if (file != null) {
            mCallback.downloadFile(message, file + "/" + fileName, itemPosition);
            return;
        }
        file = new File(Environment.getExternalStorageDirectory(), getString(R.string.app_name) + "/" + fileName);
        //noinspection ResultOfMethodCallIgnored
        file.mkdirs();
        mCallback.downloadFile(message, file.getAbsolutePath(), itemPosition);
    }

    @Override
    public void localFileClicked(Uri uri) {
        File file = getFile(uri);
        openFile(uri, FileUtils.getFileTypeFromFilePath(file.getAbsolutePath()));
    }

    @Override
    public void resendMessage(Message message) {
        mLoginPresenter.checkIfVoucherStillAvailable(this);
        if (!mChatPresenter.isAttached()) {
            mChatPresenter.attachView(this);
        }
        mCallback.resendMessage(message);
    }

    @Override
    public MediaType getMediaType(Uri fileUri) {
        return MediaType.parse(getContentResolver().getType(fileUri));
    }

    @Override
    public File getFile(Uri fileUri) {
        return FileUtils.getFile(this, fileUri);
    }

    @Override
    public void enableMessageRetry(String messageId) {
        mBaseChatAdapter.enableMessageRetry(messageId);
    }

    @Override
    public void showItemProgress(int itemPosition, int progress) {
        runOnUiThread(() -> mBaseChatAdapter.setItemProgress(itemPosition, progress));
    }

    @Override
    public void stopItemProgress(int itemPosition) {
        mBaseChatAdapter.stopItemProgress(itemPosition);
    }

    @Override
    public void fileDownloadedSuccessfully(String filePath, String fileType) {
        openFile(FileProvider.getUriForFile(BaseChatActivity.this,
                getApplicationContext().getPackageName() + ".provider",
                new File(filePath)),
                fileType);
    }

    private void openFile(Uri uri, String fileType) {
        Intent intent = new Intent();
        intent.setAction(Intent.ACTION_VIEW);
        intent.setDataAndType(uri,
                fileType);
        intent.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
        // Verify the original intent will resolve to at least one activity
        if (intent.resolveActivity(getPackageManager()) != null) {
            startActivity(intent);
        } else {
            showToastMessage(R.string.error_could_not_open_file);
        }
    }

    @Override
    public void lastMessageUpdated(Message newMessage, String oldMessageId) {
        mBaseChatAdapter.updateMessage(newMessage, oldMessageId);
    }

    @Override
    protected void onPostCreate(@Nullable Bundle savedInstanceState) {
        super.onPostCreate(savedInstanceState);
        mChatOpenedPref.set(false);
    }

    @Override
    protected void onDestroy() {
        mChatOpenedPref.set(false);
        if (mEndlessScrollListener != null) {
            mRecyclerView.removeOnScrollListener(mEndlessScrollListener);
        }
        getWindow().getDecorView().getViewTreeObserver().removeOnGlobalLayoutListener(mKeyboardListener);
        EventBus.getDefault().unregister(this);
        InjectionHelper.destroyChatComponent();
        mRecyclerView.setAdapter(null);
        mEmptyLayout.setOnTryAgainClickListener(null);
        super.onDestroy();
    }

    @Override
    public void addOneMessage(Message message) {
        mBaseChatAdapter.addMessage(message);
        if (mEndlessScrollListener.getVisibleItem() == 0) {
            mBaseChatAdapter.notifyDataSetChanged();
        }
        mRecyclerView.scrollToPosition(0);
    }

    protected void setGroupFromArguments() {
        mGroup = getIntent().getParcelableExtra(GROUP_ARG);
        cancelNotification();
        loadMessages();
        setToolbar();
    }

    @SuppressWarnings("unused")
    @Subscribe(threadMode = ThreadMode.MAIN)
    public void onInvalidToken(InvalidTokenEvent invalidTokenEvent) {
        finish();
    }

    protected void cancelNotification() {
        if (mGroup == null) {
            return;
        }
        NotificationManagerCompat notificationManager = NotificationManagerCompat.from(this);
        notificationManager.cancel((int) mGroup.id);
    }

    private ViewTreeObserver.OnGlobalLayoutListener mKeyboardListener = new ViewTreeObserver.OnGlobalLayoutListener() {

        private boolean mKeyboardShown = false;

        @Override
        public void onGlobalLayout() {
            Rect r = new Rect();
            getWindow().getDecorView().getWindowVisibleDisplayFrame(r);
            int screenHeight = getWindow().getDecorView().getHeight();

            // r.bottom is the position above soft keypad or device button.
            // if keypad is shown, the r.bottom is smaller than that before.
            int keypadHeight = screenHeight - r.bottom;

            if (keypadHeight > screenHeight * 0.15 && !mKeyboardShown) { // 0.15 ratio is perhaps enough to determine keypad height.
                // keyboard is opened
                mRecyclerView.scrollToPosition(0);
                mKeyboardShown = true;
            } else {
                mKeyboardShown = false;
            }
        }
    };
}