package org.thoughtcrime.securesms.search;

import android.annotation.SuppressLint;
import android.arch.lifecycle.ViewModelProviders;
import android.content.Intent;
import android.content.res.Configuration;
import android.os.AsyncTask;
import android.os.Bundle;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.v4.app.Fragment;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.text.TextUtils;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;

import org.thoughtcrime.securesms.ConversationActivity;
import org.thoughtcrime.securesms.ConversationListActivity;
import org.thoughtcrime.securesms.R;
import org.thoughtcrime.securesms.contacts.ContactAccessor;
import org.thoughtcrime.securesms.database.DatabaseFactory;
import org.thoughtcrime.securesms.database.ThreadDatabase;
import org.thoughtcrime.securesms.database.model.ThreadRecord;
import org.thoughtcrime.securesms.mms.GlideApp;
import org.thoughtcrime.securesms.recipients.Recipient;
import org.thoughtcrime.securesms.search.model.MessageResult;
import org.thoughtcrime.securesms.search.model.SearchResult;
import org.thoughtcrime.securesms.util.StickyHeaderDecoration;

import java.util.Locale;
import java.util.concurrent.Executors;

/**
 * A fragment that is displayed to do full-text search of messages, groups, and contacts.
 */
public class SearchFragment extends Fragment implements SearchListAdapter.EventListener {

  public static final String TAG          = "SearchFragment";
  public static final String EXTRA_LOCALE = "locale";

  private TextView               noResultsView;
  private RecyclerView           listView;
  private StickyHeaderDecoration listDecoration;

  private SearchViewModel   viewModel;
  private SearchListAdapter listAdapter;
  private String            pendingQuery;
  private Locale            locale;

  public static SearchFragment newInstance(@NonNull Locale locale) {
    Bundle args = new Bundle();
    args.putSerializable(EXTRA_LOCALE, locale);

    SearchFragment fragment = new SearchFragment();
    fragment.setArguments(args);

    return fragment;
  }

  @Override
  public void onCreate(@Nullable Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);

    this.locale = (Locale) getArguments().getSerializable(EXTRA_LOCALE);

    SearchRepository searchRepository = new SearchRepository(getContext(),
                                                             DatabaseFactory.getSearchDatabase(getContext()),
                                                             DatabaseFactory.getContactsDatabase(getContext()),
                                                             DatabaseFactory.getThreadDatabase(getContext()),
                                                             ContactAccessor.getInstance(),
                                                             Executors.newSingleThreadExecutor());
    viewModel = ViewModelProviders.of(this, new SearchViewModel.Factory(searchRepository)).get(SearchViewModel.class);

    if (pendingQuery != null) {
      viewModel.updateQuery(pendingQuery);
      pendingQuery = null;
    }
  }

  @Nullable
  @Override
  public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
    return inflater.inflate(R.layout.fragment_search, container, false);
  }

  @Override
  public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
    noResultsView = view.findViewById(R.id.search_no_results);
    listView      = view.findViewById(R.id.search_list);

    listAdapter    = new SearchListAdapter(GlideApp.with(this), this, locale);
    listDecoration = new StickyHeaderDecoration(listAdapter, false, false);

    listView.setAdapter(listAdapter);
    listView.addItemDecoration(listDecoration);
    listView.setLayoutManager(new LinearLayoutManager(getContext()));
  }

  @Override
  public void onStart() {
    super.onStart();
    viewModel.getSearchResult().observe(this, result -> {
      result = result != null ? result : SearchResult.EMPTY;

      listAdapter.updateResults(result);

      if (result.isEmpty()) {
        if (TextUtils.isEmpty(viewModel.getLastQuery().trim())) {
          noResultsView.setVisibility(View.GONE);
        } else {
          noResultsView.setVisibility(View.VISIBLE);
          noResultsView.setText(getString(R.string.SearchFragment_no_results, viewModel.getLastQuery()));
        }
      } else {
        noResultsView.setVisibility(View.VISIBLE);
        noResultsView.setText("");
      }
    });
  }

  @Override
  public void onConfigurationChanged(Configuration newConfig) {
    super.onConfigurationChanged(newConfig);

    if (listDecoration != null) {
      listDecoration.invalidateLayouts();
    }
  }


  @Override
  public void onConversationClicked(@NonNull ThreadRecord threadRecord) {
    ConversationListActivity conversationList = (ConversationListActivity) getActivity();

    if (conversationList != null) {
      conversationList.onCreateConversation(threadRecord.getThreadId(),
                                            threadRecord.getRecipient(),
                                            threadRecord.getDistributionType(),
                                            threadRecord.getLastSeen());
    }
  }

  @Override
  public void onContactClicked(@NonNull Recipient contact) {
    Intent intent = new Intent(getContext(), ConversationActivity.class);
    intent.putExtra(ConversationActivity.ADDRESS_EXTRA, contact.getAddress());

    long existingThread = DatabaseFactory.getThreadDatabase(getContext()).getThreadIdIfExistsFor(contact);

    intent.putExtra(ConversationActivity.THREAD_ID_EXTRA, existingThread);
    intent.putExtra(ConversationActivity.DISTRIBUTION_TYPE_EXTRA, ThreadDatabase.DistributionTypes.DEFAULT);
    startActivity(intent);
  }

  @SuppressLint("StaticFieldLeak")
  @Override
  public void onMessageClicked(@NonNull MessageResult message) {
    new AsyncTask<Void, Void, Integer>() {
      @Override
      protected Integer doInBackground(Void... voids) {
        int  startingPosition = DatabaseFactory.getMmsSmsDatabase(getContext()).getMessagePositionInConversation(message.threadId, message.receivedTimestampMs);
        startingPosition = Math.max(0, startingPosition);

        return startingPosition;
      }

      @Override
      protected void onPostExecute(Integer startingPosition) {
        ConversationListActivity conversationList = (ConversationListActivity) getActivity();
        if (conversationList != null) {
          conversationList.openConversation(message.threadId,
                                            message.recipient,
                                            ThreadDatabase.DistributionTypes.DEFAULT,
                                            -1,
                                            startingPosition);
        }
      }
    }.execute();
  }

  public void updateSearchQuery(@NonNull String query) {
    if (viewModel != null) {
      viewModel.updateQuery(query);
    } else {
      pendingQuery = query;
    }
  }
}
