package com.github.florent37.inlineactivityresult;

import android.content.Intent;
import android.os.Bundle;

import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.RestrictTo;
import androidx.fragment.app.Fragment;

import com.github.florent37.inlineactivityresult.request.Request;
import com.github.florent37.inlineactivityresult.request.RequestFabric;

/**
 * DO NOT USE THIS FRAGMENT DIRECTLY!
 * It's only here because fragments have to be public
 */
@RestrictTo(RestrictTo.Scope.LIBRARY)
public class ActivityResultFragment extends Fragment {

    private static final String INTENT_TO_START = "INTENT_TO_START";

    private static final int REQUEST_CODE = 24; // [0;65535]

    @Nullable
    private Request request;

    @Nullable
    private ActivityResultListener listener;

    public ActivityResultFragment() {
        setRetainInstance(true);
    }

    public static ActivityResultFragment newInstance(@NonNull final Intent intent, @Nullable ActivityResultListener listener) {
        return newInstance(RequestFabric.create(intent), listener);
    }

    public static ActivityResultFragment newInstance(@NonNull final Request request, @Nullable ActivityResultListener listener) {
        final ActivityResultFragment fragment = new ActivityResultFragment();

        fragment.setRequest(request);
        fragment.setListener(listener);

        return fragment;
    }

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

        loadArguments();
    }

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

        //Execute Request
        executeRequest();
    }

    @Override
    public void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) {
        super.onActivityResult(requestCode, resultCode, data);

        if (requestCode == REQUEST_CODE) {
            if (listener != null) {
                listener.onActivityResult(requestCode, resultCode, data);
            }

            removeFragment();
        }
    }

    public void executeRequest() {
        if (request != null) {
            try {
                request.execute(this, REQUEST_CODE);
            } catch (Exception e) {
                e.printStackTrace();

                if (listener != null) {
                    listener.error(e);
                }

                removeFragment();
            }
        } else {
            // this shouldn't happen, but just to be sure
            if (listener != null) {
                listener.error(new NullPointerException("request is empty"));
            }
            removeFragment();
        }
    }

    private void setRequest(@NonNull Request request) {
        this.request = request;

        saveArguments();
    }

    private void setListener(@Nullable ActivityResultListener listener) {
        if (listener != null) {
            this.listener = listener;
        }
    }

    private void removeFragment() {
        if (getFragmentManager() != null) {
            getFragmentManager().beginTransaction()
                    .remove(this)
                    .commitAllowingStateLoss();
        }
    }

    private void loadArguments() {
        Bundle arguments = getArguments();

        if (arguments != null) {
            this.request = arguments.getParcelable(INTENT_TO_START);
        }
    }

    private void saveArguments() {
        Bundle args = new Bundle();
        args.putParcelable(INTENT_TO_START, request);

        setArguments(args);
    }

    private int checkForValidRequestCode(int requestCode) {
        if ((requestCode & 0xffff0000) != 0) {
            throw new IllegalArgumentException("Can only use lower 16 bits for requestCode=" + requestCode);
        }

        return requestCode;
    }

    interface ActivityResultListener {
        void onActivityResult(int requestCode, int resultCode, Intent data);

        void error(Throwable throwable);
    }
}