package app.trigger.ssh;

import app.trigger.Log;
import app.trigger.Utils;
import com.github.isabsent.filepicker.SimpleFilePickerDialog;
import static com.github.isabsent.filepicker.SimpleFilePickerDialog.CompositeMode.FOLDER_ONLY_SINGLE_CHOICE;

import android.content.DialogInterface;
import android.os.Bundle;
import android.os.Environment;
import android.support.annotation.NonNull;
import android.support.v7.app.AlertDialog;
import android.support.v7.app.AppCompatActivity;
import android.view.View;
import android.widget.ArrayAdapter;
import android.widget.Button;
import android.widget.EditText;
import android.widget.Spinner;
import android.widget.TextView;
import android.widget.Toast;

import app.trigger.R;
import com.jcraft.jsch.JSch;
import com.jcraft.jsch.KeyPair;


public class KeyPairActivity extends AppCompatActivity implements
        SimpleFilePickerDialog.InteractionListenerString,
        RegisterIdentityTask.OnTaskCompleted,
        GenerateIdentityTask.OnTaskCompleted {
    private static final String SELECT_PATH_REQUEST = "SELECT_PATH_REQUEST";
    private static final int REQUEST_PERMISSION = 0x01;
    private KeyPairPreference preference; // hack
    private AlertDialog.Builder builder;
    private Button createButton;
    private Button importButton;
    private Button exportButton;
    private Button cancelButton;
    private Button selectButton;
    private Button registerButton;
    private Button okButton;
    private Button deleteButton;
    private TextView fingerprint;
    private TextView publicKey;
    private TextView pathSelection;
    private EditText registerAddress;
    private Spinner keypairStrengthSpinner;

    private KeyPair keypair;
    private String selected_path;
    private boolean keyGenInProgress = false;

    private void showErrorMessage(String title, String message) {
        builder.setTitle(title);
        builder.setMessage(message);
        builder.setPositiveButton(android.R.string.ok, null);
        builder.show();
    }

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_keypair);

        this.preference = KeyPairPreference.self; // hack, TODO: pass serialized key in bundle
        this.keypair = this.preference.getKeyPair();

        builder = new AlertDialog.Builder(this);
        createButton = findViewById(R.id.CreateButton);
        importButton = findViewById(R.id.ImportButton);
        exportButton = findViewById(R.id.ExportButton);
        cancelButton = findViewById(R.id.CancelButton);
        selectButton = findViewById(R.id.SelectButton);
        registerButton = findViewById(R.id.RegisterButton);
        okButton = findViewById(R.id.OkButton);
        deleteButton = findViewById(R.id.DeleteButton);
        fingerprint = findViewById(R.id.Fingerprint);
        publicKey = findViewById(R.id.PublicKey);
        pathSelection = findViewById(R.id.PathSelection);
        registerAddress = findViewById(R.id.RegisterAddress);
        keypairStrengthSpinner = findViewById(R.id.keypair_strength_spinner);
        final KeyPairActivity self = this;

        ArrayAdapter<String> dataAdapter = new ArrayAdapter<String>(this,
                android.R.layout.simple_spinner_item,
                getResources().getStringArray(R.array.KeypairStrengths)
        );
        dataAdapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
        keypairStrengthSpinner.setAdapter(dataAdapter);
        keypairStrengthSpinner.setSelection(1);

        registerAddress.setText(
            getIntent().getStringExtra("register_url")
        );

        registerButton.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                String address = registerAddress.getText().toString();
                if (address == null || address.length() == 0) {
                    showErrorMessage("Address Empty", "Address and port needed to send public key to destination.");
                } else if (keypair == null) {
                    showErrorMessage("Key Pair Empty", "No public key available to register.");
                } else {
                    RegisterIdentityTask task = new RegisterIdentityTask(self, address, keypair);
                    task.start();
                }
            }
        });

        createButton.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                if (keyGenInProgress) {
                    showErrorMessage("In Progress", "Key generation already in progress. Please wait.");
                } else {
                    keyGenInProgress = true;
                    int key_strength = 0;
                    switch (self.keypairStrengthSpinner.getSelectedItemPosition()) {
                        case 0:
                            key_strength = 1024;
                            break;
                        case 1:
                            key_strength = 2048;
                            break;
                        case 2:
                            key_strength = 4096;
                            break;
                        default:
                            Log.e("KeyPairActivity", "Invalid selected item position");
                            key_strength = 0;
                            break;
                    }

                    if (key_strength > 0) {
                        new GenerateIdentityTask(self).execute(key_strength);
                    }
                }
            }
        });

        selectButton.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                if (Utils.hasReadPermission(self)) {
                    final String rootPath = Environment.getExternalStorageDirectory().getAbsolutePath();
                    showListItemDialog("Pick Directory", rootPath, FOLDER_ONLY_SINGLE_CHOICE, SELECT_PATH_REQUEST);
                } else {
                    Utils.requestReadPermission(self, REQUEST_PERMISSION);
                    Utils.requestWritePermission(self, REQUEST_PERMISSION);
                }
            }
        });

        exportButton.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                exportKeys();
            }
        });

        importButton.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                importKeys();
            }
        });

        okButton.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                // persist your value here
                preference.setKeyPair(self.keypair);
                self.finish();
            }
        });

        deleteButton.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                builder.setTitle("Confirm");
                builder.setMessage("Really remove key pair?");
                builder.setCancelable(false); // not necessary

                builder.setPositiveButton(R.string.yes, new DialogInterface.OnClickListener() {
                    public void onClick(DialogInterface dialog, int id) {
                        KeyPairActivity.this.keypair = null;
                        updateKeyInfo();
                        dialog.cancel();
                    }
                });

                builder.setNegativeButton(R.string.no, new DialogInterface.OnClickListener() {
                    public void onClick(DialogInterface dialog, int id) {
                        dialog.cancel();
                    }
                });

                // create dialog box
                AlertDialog alert = builder.create();
                alert.show();
            }
        });

        cancelButton.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                // persist your value here
                self.finish();
            }
        });

        updateKeyInfo();
        updatePathInfo();
    }

    @Override
    public void onGenerateIdentityTaskCompleted(String message, KeyPair keypair) {
        if (keypair != null) {
            // only set if successful
            this.keypair = keypair;
        }

        this.keyGenInProgress = false;
        Toast.makeText(getApplicationContext(), message, Toast.LENGTH_SHORT).show();
        updateKeyInfo();
    }

    @Override
    public void onRegisterIdentityTaskCompleted(String message) {
        this.runOnUiThread(() -> {
            Toast.makeText(getApplicationContext(), message, Toast.LENGTH_SHORT).show();
        });
    }

    private void exportKeys() {
        if (selected_path == null) {
            showErrorMessage("No Directory Selected", "No directory for export selected.");
        } else if (keypair == null) {
            showErrorMessage("No Key Pair", "No keys loaded to export.");
        } else if (!Utils.hasWritePermission(this)) {
            Utils.requestWritePermission(this, REQUEST_PERMISSION);
        } else try {
            SshTools.KeyPairData data = SshTools.keypairToBytes(KeyPairActivity.this.keypair);

            Utils.writeExternalFile(selected_path + "/id_rsa.pub", data.pubkey);
            Utils.writeExternalFile(selected_path + "/id_rsa", data.prvkey);

            Toast.makeText(getApplicationContext(), "Done. Wrote files 'id_rsa.pub' and 'id_rsa'.", Toast.LENGTH_SHORT).show();
        } catch (Exception e) {
            showErrorMessage("Error", e.getMessage());
        }
    }

    private void importKeys() {
        if (selected_path == null) {
            showErrorMessage("No Directory Selected", "No directory for import selected.");
        } else if (!Utils.hasReadPermission(this)) {
            Utils.requestReadPermission(this, REQUEST_PERMISSION);
        } else try {
            byte[] prvkey = Utils.readExternalFile(selected_path + "/id_rsa");
            byte[] pubkey = Utils.readExternalFile(selected_path + "/id_rsa.pub");

            JSch jsch = new JSch();
            KeyPairActivity.this.keypair = KeyPair.load(jsch, prvkey, pubkey);

            Toast.makeText(getApplicationContext(), "Done. Read 'id_rsa.pub' and 'id_rsa'.", Toast.LENGTH_SHORT).show();
        } catch (Exception e) {
            showErrorMessage("Error", e.getMessage());
        }
    }

    // path picker
    @Override
    public void showListItemDialog(String title, String folderPath, SimpleFilePickerDialog.CompositeMode mode, String dialogTag){
        SimpleFilePickerDialog.build(folderPath, mode)
                .title(title)
                .show(this, dialogTag);
    }

    @Override
    public boolean onResult(@NonNull String dialogTag, int which, @NonNull Bundle extras) {
        switch (dialogTag) {
            case SELECT_PATH_REQUEST:
                if (extras.containsKey(SimpleFilePickerDialog.SELECTED_SINGLE_PATH)) {
                    String selectedSinglePath = extras.getString(SimpleFilePickerDialog.SELECTED_SINGLE_PATH);
                    this.selected_path = selectedSinglePath;
                    updatePathInfo();
                }
                break;
        }
        return false;
    }

    private void updateKeyInfo() {
        if (keypair == null) {
            deleteButton.setEnabled(false);
            fingerprint.setText("<no key loaded>");
            publicKey.setText("<no key loaded>");
        } else {
            SshTools.KeyPairData data = SshTools.keypairToBytes(keypair);

            deleteButton.setEnabled(true);
            fingerprint.setText(keypair.getFingerPrint());
            publicKey.setText(new String(data.pubkey));
        }
    }

    private void updatePathInfo() {
        if (selected_path == null) {
            pathSelection.setText("<no path selected>");
            exportButton.setEnabled(false);
            importButton.setEnabled(false);
        } else {
            pathSelection.setText(this.selected_path);
            exportButton.setEnabled(true);
            importButton.setEnabled(true);
        }
    }

    @Override
    public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {
        switch (requestCode) {
            case REQUEST_PERMISSION:
                if (Utils.allGranted(grantResults)) {
                    // permissions granted
                    Toast.makeText(getApplicationContext(), "Permissions granted - please try again.", Toast.LENGTH_SHORT).show();
                } else {
                    showErrorMessage("Permissions Required", "Action cannot be performed.");
                }
                break;
        }
    }
}