package org.joinmastodon.android;

import android.Manifest;
import android.app.Fragment;
import android.app.assist.AssistContent;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.net.Uri;
import android.os.Build;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.FrameLayout;
import android.widget.Toast;

import org.joinmastodon.android.api.ObjectValidationException;
import org.joinmastodon.android.api.requests.search.GetSearchResults;
import org.joinmastodon.android.api.session.AccountSession;
import org.joinmastodon.android.api.session.AccountSessionManager;
import org.joinmastodon.android.fragments.ComposeFragment;
import org.joinmastodon.android.fragments.HomeFragment;
import org.joinmastodon.android.fragments.ProfileFragment;
import org.joinmastodon.android.fragments.ThreadFragment;
import org.joinmastodon.android.fragments.onboarding.AccountActivationFragment;
import org.joinmastodon.android.fragments.onboarding.CustomWelcomeFragment;
import org.joinmastodon.android.model.Notification;
import org.joinmastodon.android.model.SearchResults;
import org.joinmastodon.android.ui.utils.UiUtils;
import org.joinmastodon.android.updater.GithubSelfUpdater;
import org.joinmastodon.android.utils.ProvidesAssistContent;
import org.parceler.Parcels;

import androidx.annotation.Nullable;
import me.grishka.appkit.FragmentStackActivity;
import me.grishka.appkit.Nav;
import me.grishka.appkit.api.Callback;
import me.grishka.appkit.api.ErrorResponse;

public class MainActivity extends FragmentStackActivity implements ProvidesAssistContent {
	@Override
	protected void onCreate(@Nullable Bundle savedInstanceState){
		UiUtils.setUserPreferredTheme(this);
		super.onCreate(savedInstanceState);

		if(savedInstanceState==null){
			if(AccountSessionManager.getInstance().getLoggedInAccounts().isEmpty()){
				showFragmentClearingBackStack(new CustomWelcomeFragment());
			}else{
				AccountSession session;
				Bundle args=new Bundle();
				Intent intent=getIntent();
				if(intent.hasExtra("fromExternalShare")) {
					AccountSessionManager.getInstance()
							.setLastActiveAccountID(intent.getStringExtra("account"));
					AccountSessionManager.getInstance().maybeUpdateLocalInfo(
							AccountSessionManager.getInstance().getLastActiveAccount());
					showFragmentForExternalShare(intent.getExtras());
					return;
				}

				boolean fromNotification = intent.getBooleanExtra("fromNotification", false);
				boolean hasNotification = intent.hasExtra("notification");
				if(fromNotification){
					String accountID=intent.getStringExtra("accountID");
					try{
						session=AccountSessionManager.getInstance().getAccount(accountID);
						if(!hasNotification) args.putString("tab", "notifications");
					}catch(IllegalStateException x){
						session=AccountSessionManager.getInstance().getLastActiveAccount();
					}
				}else{
					session=AccountSessionManager.getInstance().getLastActiveAccount();
				}
				AccountSessionManager.getInstance().maybeUpdateLocalInfo(session);
				args.putString("account", session.getID());
				Fragment fragment=session.activated ? new HomeFragment() : new AccountActivationFragment();
				fragment.setArguments(args);
				if(fromNotification && hasNotification){
					Notification notification=Parcels.unwrap(intent.getParcelableExtra("notification"));
					showFragmentForNotification(notification, session.getID());
				} else if (intent.getBooleanExtra("compose", false)){
					showCompose();
				} else if (Intent.ACTION_VIEW.equals(intent.getAction())){
					handleURL(intent.getData(), null);
				} else {
					showFragmentClearingBackStack(fragment);
					maybeRequestNotificationsPermission();
				}
			}
		}

		if(GithubSelfUpdater.needSelfUpdating()){
			GithubSelfUpdater.getInstance().maybeCheckForUpdates();
		}
	}

	@Override
	protected void onNewIntent(Intent intent){
		super.onNewIntent(intent);
		AccountSessionManager.getInstance().maybeUpdateLocalInfo();
		if (intent.hasExtra("fromExternalShare")) showFragmentForExternalShare(intent.getExtras());
		else if (intent.getBooleanExtra("fromNotification", false)) {
			String accountID=intent.getStringExtra("accountID");
			try{
				AccountSessionManager.getInstance().getAccount(accountID);
			}catch(IllegalStateException x){
				return;
			}
			if(intent.hasExtra("notification")){
				Notification notification=Parcels.unwrap(intent.getParcelableExtra("notification"));
				showFragmentForNotification(notification, accountID);
			}else{
				AccountSessionManager.getInstance().setLastActiveAccountID(accountID);
				Bundle args=new Bundle();
				args.putString("account", accountID);
				args.putString("tab", "notifications");
				Fragment fragment=new HomeFragment();
				fragment.setArguments(args);
				showFragmentClearingBackStack(fragment);
			}
		}else if(intent.getBooleanExtra("compose", false)){
			showCompose();
		}else if(Intent.ACTION_VIEW.equals(intent.getAction())){
			handleURL(intent.getData(), null);
		}/*else if(intent.hasExtra(PackageInstaller.EXTRA_STATUS) && GithubSelfUpdater.needSelfUpdating()){
			GithubSelfUpdater.getInstance().handleIntentFromInstaller(intent, this);
		}*/
	}

	public void handleURL(Uri uri, String accountID){
		if(uri==null)
			return;
		if(!"https".equals(uri.getScheme()) && !"http".equals(uri.getScheme()))
			return;
		AccountSession session;
		if(accountID==null)
			session=AccountSessionManager.getInstance().getLastActiveAccount();
		else
			session=AccountSessionManager.get(accountID);
		if(session==null || !session.activated)
			return;
		openSearchQuery(uri.toString(), session.getID(), R.string.opening_link, false);
	}

	public void openSearchQuery(String q, String accountID, int progressText, boolean fromSearch){
		new GetSearchResults(q, null, true)
				.setCallback(new Callback<>(){
					@Override
					public void onSuccess(SearchResults result){
						Bundle args=new Bundle();
						args.putString("account", accountID);
						if(result.statuses!=null && !result.statuses.isEmpty()){
							args.putParcelable("status", Parcels.wrap(result.statuses.get(0)));
							Nav.go(MainActivity.this, ThreadFragment.class, args);
						}else if(result.accounts!=null && !result.accounts.isEmpty()){
							args.putParcelable("profileAccount", Parcels.wrap(result.accounts.get(0)));
							Nav.go(MainActivity.this, ProfileFragment.class, args);
						}else{
							Toast.makeText(MainActivity.this, fromSearch ? R.string.no_search_results : R.string.link_not_supported, Toast.LENGTH_SHORT).show();
						}
					}

					@Override
					public void onError(ErrorResponse error){
						error.showToast(MainActivity.this);
					}
				})
				.wrapProgress(this, progressText, true)
				.exec(accountID);
	}

	private void showFragmentForNotification(Notification notification, String accountID){
		try{
			notification.postprocess();
		}catch(ObjectValidationException x){
			Log.w("MainActivity", x);
			return;
		}
		Bundle args = new Bundle();
		args.putBoolean("noTransition", true);
		UiUtils.showFragmentForNotification(this, notification, accountID, args);
	}

	private void showFragmentForExternalShare(Bundle args) {
		String className = args.getString("fromExternalShare");
		Fragment fragment = switch (className) {
			case "ThreadFragment" -> new ThreadFragment();
			case "ProfileFragment" -> new ProfileFragment();
			default -> null;
		};
		if (fragment == null) return;
		args.putBoolean("_can_go_back", true);
		fragment.setArguments(args);
		showFragment(fragment);
	}

	private void showCompose(){
		AccountSession session=AccountSessionManager.getInstance().getLastActiveAccount();
		if(session==null || !session.activated)
			return;
		ComposeFragment compose=new ComposeFragment();
		Bundle composeArgs=new Bundle();
		composeArgs.putString("account", session.getID());
		compose.setArguments(composeArgs);
		showFragment(compose);
	}

	private void maybeRequestNotificationsPermission(){
		if(Build.VERSION.SDK_INT>=Build.VERSION_CODES.TIRAMISU && checkSelfPermission(Manifest.permission.POST_NOTIFICATIONS)!=PackageManager.PERMISSION_GRANTED){
			requestPermissions(new String[]{Manifest.permission.POST_NOTIFICATIONS}, 100);
		}
	}

	/**
	 * when opening app through a notification: if (thread) fragment "can go back", clear back stack
	 * and show home fragment. upstream's implementation doesn't require this as it opens home first
	 * and then immediately switches to the notification's ThreadFragment. this causes a black
	 * screen in megalodon, for some reason, so i'm working around this that way.
 	 */
	@Override
	public void onBackPressed() {
		Fragment currentFragment = getFragmentManager().findFragmentById(
				(fragmentContainers.get(fragmentContainers.size() - 1)).getId()
		);
		Bundle currentArgs = currentFragment.getArguments();
		if (fragmentContainers.size() != 1
				|| currentArgs == null
				|| !currentArgs.getBoolean("_can_go_back", false)) {
			super.onBackPressed();
			return;
		}
		if (currentArgs.getBoolean("_finish_on_back", false)) {
			finish();
		} else if (currentArgs.containsKey("account")) {
			Bundle args = new Bundle();
			args.putString("account", currentArgs.getString("account"));
			if (getIntent().getBooleanExtra("fromNotification", false)) {
				args.putString("tab", "notifications");
			}
			Fragment fragment=new HomeFragment();
			fragment.setArguments(args);
			showFragmentClearingBackStack(fragment);
		}
	}

	public Fragment getCurrentFragment() {
		for (int i = fragmentContainers.size() - 1; i >= 0; i--) {
			FrameLayout fl = fragmentContainers.get(i);
			if (fl.getVisibility() == View.VISIBLE) {
				return getFragmentManager().findFragmentById(fl.getId());
			}
		}
		return null;
	}

	@Override
	public void onProvideAssistContent(AssistContent assistContent) {
		super.onProvideAssistContent(assistContent);
		Fragment fragment = getCurrentFragment();
		if (fragment != null) callFragmentToProvideAssistContent(fragment, assistContent);
	}
}
