/*
 * Copyright 2019 Daniel Gultsch
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package rs.ltt.android.ui.activity;

import android.content.Intent;
import android.net.Uri;
import android.os.Bundle;
import android.text.Editable;
import android.text.TextWatcher;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.view.inputmethod.InputMethodManager;
import android.widget.EditText;

import androidx.annotation.NonNull;
import androidx.appcompat.app.ActionBar;
import androidx.appcompat.app.AppCompatActivity;
import androidx.appcompat.widget.AppCompatEditText;
import androidx.databinding.DataBindingUtil;
import androidx.fragment.app.Fragment;
import androidx.lifecycle.ViewModelProvider;

import com.google.android.material.dialog.MaterialAlertDialogBuilder;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.UUID;

import rs.ltt.android.R;
import rs.ltt.android.databinding.ActivityComposeBinding;
import rs.ltt.android.ui.ChipDrawableSpan;
import rs.ltt.android.ui.ComposeAction;
import rs.ltt.android.ui.model.ComposeViewModel;
import rs.ltt.android.ui.model.ComposeViewModelFactory;
import rs.ltt.android.util.SetupCache;
import rs.ltt.jmap.mua.util.MailToUri;

//TODO handle save instance state
public class ComposeActivity extends AppCompatActivity {

    public static final int REQUEST_EDIT_DRAFT = 0x100;
    public static final String EDITING_TASK_ID_EXTRA = "work_request_id";
    public static final String DISCARDED_THREAD_EXTRA = "discarded_thread";
    private static final Logger LOGGER = LoggerFactory.getLogger(ComposeActivity.class);
    private static final String ACCOUNT_EXTRA = "account";
    private static final String COMPOSE_ACTION_EXTRA = "compose_action";
    private static final String EMAIL_ID_EXTRA = "email_id";
    private ActivityComposeBinding binding;
    private ComposeViewModel composeViewModel;

    public static void editDraft(final Fragment fragment, Long account, final String emailId) {
        launch(fragment, account, emailId, ComposeAction.EDIT_DRAFT);
    }

    public static void replyAll(final Fragment fragment, Long account, final String emailId) {
        launch(fragment, account, emailId, ComposeAction.REPLY_ALL);
    }

    private static void launch(final Fragment fragment,
                               final Long account,
                               final String emailId,
                               final ComposeAction action) {
        final Intent intent = new Intent(fragment.getContext(), ComposeActivity.class);
        intent.putExtra(ACCOUNT_EXTRA, account);
        intent.putExtra(COMPOSE_ACTION_EXTRA, action.toString());
        intent.putExtra(EMAIL_ID_EXTRA, emailId);
        fragment.startActivityForResult(intent, REQUEST_EDIT_DRAFT);
    }

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

        if (!SetupCache.hasAccounts(this)) {
            redirectToSetupActivity();
            finishAffinity();
            return;
        }

        binding = DataBindingUtil.setContentView(this, R.layout.activity_compose);

        setupActionBar();

        final ViewModelProvider viewModelProvider = new ViewModelProvider(
                this,
                new ComposeViewModelFactory(
                        getApplication(),
                        getViewModelParameter(savedInstanceState)
                )
        );
        composeViewModel = viewModelProvider.get(ComposeViewModel.class);

        composeViewModel.getErrorMessage().observe(this, event -> {
            if (event.isConsumable()) {
                final String message = event.consume();
                new MaterialAlertDialogBuilder(this)
                        .setTitle(message)
                        .setPositiveButton(R.string.ok, null)
                        .show();
            }
        });

        binding.setComposeViewModel(composeViewModel);
        binding.setLifecycleOwner(this);

        binding.to.addTextChangedListener(new ChipTextWatcher(binding.to));
        binding.to.setOnFocusChangeListener(
                (v, hasFocus) -> ChipDrawableSpan.apply(this, binding.to.getEditableText(), hasFocus)
        );

        binding.cc.addTextChangedListener(new ChipTextWatcher(binding.cc));
        binding.cc.setOnFocusChangeListener(
                (v, hasFocus) -> ChipDrawableSpan.apply(this, binding.cc.getEditableText(), hasFocus)
        );

        binding.moreAddresses.setOnClickListener((v -> composeViewModel.showExtendedAddresses()));

        binding.subject.setOnFocusChangeListener(this::focusOnBodyOrSubject);
        binding.body.setOnFocusChangeListener(this::focusOnBodyOrSubject);

        binding.toLabel.setOnClickListener(v -> requestFocusAndOpenKeyboard(binding.to));
        binding.placeholder.setOnClickListener(v -> requestFocusAndOpenKeyboard(binding.body));

        //TODO once we handle instance state ourselves we need to call ChipDrawableSpan.reset() on `to`
    }

    private void redirectToSetupActivity() {
        final Intent currentIntent = getIntent();
        final Uri data = currentIntent == null ? null : currentIntent.getData();
        final MailToUri uri = data == null ? null : MailToUri.parse(data.toString());
        final Intent nextIntent = new Intent(this, SetupActivity.class);
        if (uri != null) {
            nextIntent.putExtra(SetupActivity.EXTRA_NEXT_ACTION, data.toString());
        }
        startActivity(nextIntent);
    }

    private ComposeViewModel.Parameter getViewModelParameter(final Bundle savedInstanceState) {
        final boolean freshStart = savedInstanceState == null || savedInstanceState.isEmpty();
        final Intent i = getIntent();
        final MailToUri uri = i != null ? getUri(i) : null;
        if (uri != null) {
            return new ComposeViewModel.Parameter(uri, freshStart);
        }
        final Long account;
        if (i != null && i.hasExtra(ACCOUNT_EXTRA)) {
            account = i.getLongExtra(ACCOUNT_EXTRA, 0L);
        } else {
            account = null;
        }
        final ComposeAction action = ComposeAction.of(i == null ? null : i.getStringExtra(COMPOSE_ACTION_EXTRA));
        final String emailId = i == null ? null : i.getStringExtra(EMAIL_ID_EXTRA);
        return new ComposeViewModel.Parameter(account, freshStart, action, emailId);
    }

    private static MailToUri getUri(@NonNull final Intent intent) {
        final Uri data = intent.getData();
        if (data == null) {
            return null;
        }
        try {
            return MailToUri.get(data.toString());
        } catch (IllegalArgumentException e) {
            LOGGER.warn("activity was called with invalid URI {}. {}", data.toString(), e.getMessage());
            return null;
        }
    }

    private void focusOnBodyOrSubject(final View view, final boolean hasFocus) {
        if (hasFocus) {
            composeViewModel.suggestHideExtendedAddresses();
        }
    }

    private void requestFocusAndOpenKeyboard(AppCompatEditText editText) {
        editText.requestFocus();
        final InputMethodManager inputMethodManager = getSystemService(InputMethodManager.class);
        if (inputMethodManager != null) {
            inputMethodManager.showSoftInput(editText, InputMethodManager.SHOW_IMPLICIT);
        }
    }

    @Override
    public boolean onCreateOptionsMenu(@NonNull Menu menu) {
        getMenuInflater().inflate(R.menu.activity_compose, menu);
        return super.onCreateOptionsMenu(menu);
    }

    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        switch (item.getItemId()) {
            case android.R.id.home:
                setResultIntent();
                break;
            case R.id.action_send:
                if (composeViewModel.send()) {
                    finish();
                }
                break;
            case R.id.action_discard:
                discardDraft();
                break;
        }
        return super.onOptionsItemSelected(item);

    }

    @Override
    public void onBackPressed() {
        setResultIntent();
        super.onBackPressed();
    }

    private void setResultIntent() {
        final UUID uuid = composeViewModel == null ? null : composeViewModel.saveDraft();
        if (uuid != null) {
            LOGGER.info("Storing draft saving worker task uuid");
            final Intent intent = new Intent();
            intent.putExtra(EDITING_TASK_ID_EXTRA, uuid);
            setResult(RESULT_OK, intent);
        }
    }

    private void discardDraft() {
        final boolean isOnlyEmailInThread = composeViewModel.discard();
        Intent intent = new Intent();
        intent.putExtra(DISCARDED_THREAD_EXTRA, isOnlyEmailInThread);
        setResult(RESULT_OK, intent);
        finish();
    }

    @Override
    public void onDestroy() {
        if (isFinishing() && composeViewModel != null) {
            composeViewModel.saveDraft();
        }
        super.onDestroy();
    }

    private void setupActionBar() {
        setSupportActionBar(binding.toolbar);
        final ActionBar actionbar = requireActionBar();
        final boolean displayUpButton = !isTaskRoot();
        actionbar.setHomeButtonEnabled(displayUpButton);
        actionbar.setDisplayHomeAsUpEnabled(displayUpButton);
    }

    private @NonNull
    ActionBar requireActionBar() {
        final ActionBar actionBar = getSupportActionBar();
        if (actionBar == null) {
            throw new IllegalStateException("No ActionBar found");
        }
        return actionBar;
    }

    private static class ChipTextWatcher implements TextWatcher {

        private final EditText editText;

        private ChipTextWatcher(EditText editText) {
            this.editText = editText;
        }

        @Override
        public void beforeTextChanged(CharSequence s, int start, int count, int after) {

        }

        @Override
        public void onTextChanged(CharSequence s, int start, int before, int count) {

        }

        @Override
        public void afterTextChanged(Editable editable) {
            ChipDrawableSpan.apply(editText.getContext(), editable, editText.hasFocus());
        }
    }
}