package com.secureline.notes;

import android.app.Activity;
import android.content.Intent;
import android.content.SharedPreferences;
import android.os.Bundle;
import android.os.SystemClock;
import android.text.InputType;
import android.view.View;
import android.view.WindowManager;
import android.widget.Button;
import android.widget.EditText;
import android.widget.TextView;
import android.widget.Toast;

import com.secureline.notes.data.NotesRepository;
import com.secureline.notes.security.SessionManager;
import com.secureline.notes.ui.NoteEditorActivity;
import com.secureline.notes.ui.NotesListActivity;

import java.io.File;
import java.security.SecureRandom;
import java.security.spec.KeySpec;
import java.util.Base64;

import javax.crypto.SecretKeyFactory;
import javax.crypto.spec.PBEKeySpec;

public class LockActivity extends Activity {

    private static final String PREF = "secureline_notes_lock";
    private static final String KEY_HASH = "pwd_hash";
    private static final String KEY_SALT = "pwd_salt";
    private static final String KEY_LAST_FAIL = "last_fail";
    private static final String KEY_FAIL_COUNT = "fail_count";

    private static final long DELAY_MS = 10000L;
    private static final int MAX_ATTEMPTS = 10;
    private static final int MIN_LENGTH = 8;
    private static final int ITERATIONS = 250000;
    private static final int KEY_LENGTH = 256;

    private EditText passwordInput;
    private EditText confirmInput;

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

        getWindow().setFlags(
                WindowManager.LayoutParams.FLAG_SECURE,
                WindowManager.LayoutParams.FLAG_SECURE
        );

        setContentView(R.layout.activity_lock);

        final SharedPreferences prefs = getSharedPreferences(PREF, MODE_PRIVATE);

        final TextView info = findViewById(R.id.info);
        passwordInput = findViewById(R.id.pin_input);
        confirmInput = findViewById(R.id.confirm_input);
        final Button actionBtn = findViewById(R.id.action_button);

        int flags = InputType.TYPE_CLASS_TEXT
                | InputType.TYPE_TEXT_VARIATION_PASSWORD
                | InputType.TYPE_TEXT_FLAG_NO_SUGGESTIONS;

        passwordInput.setInputType(flags);
        confirmInput.setInputType(flags);

        passwordInput.setImportantForAutofill(View.IMPORTANT_FOR_AUTOFILL_NO);
        confirmInput.setImportantForAutofill(View.IMPORTANT_FOR_AUTOFILL_NO);

        passwordInput.setLongClickable(false);
        passwordInput.setTextIsSelectable(false);
        confirmInput.setLongClickable(false);
        confirmInput.setTextIsSelectable(false);

        final String storedHash = prefs.getString(KEY_HASH, null);

	if (storedHash == null) {
	    info.setText(R.string.lock_create_description);
	    confirmInput.setVisibility(View.VISIBLE);
	    actionBtn.setText(R.string.create_password);
	} else {
	    info.setText(R.string.lock_unlock_description);
	    confirmInput.setVisibility(View.GONE);
	    actionBtn.setText(R.string.unlock);
	}

        actionBtn.setOnClickListener(v -> {
            try {
                final String password = passwordInput.getText().toString().trim();

		if (password.length() < MIN_LENGTH) {
		    toast(R.string.password_minimum_error);
		    passwordInput.requestFocus();
		    wipeInputs();
		    return;
		}

                final String currentHash = prefs.getString(KEY_HASH, null);
                final String currentSalt = prefs.getString(KEY_SALT, null);

                if (currentHash == null) {
                    final String confirm = confirmInput.getText().toString().trim();

		    if (!password.equals(confirm)) {
		        toast(R.string.passwords_do_not_match);
		        confirmInput.requestFocus();
		        wipeInputs();
		        return;
		    }

                    final byte[] salt = generateSalt();
                    final byte[] hash = derive(password, salt);

                    prefs.edit()
                            .putString(KEY_HASH, Base64.getEncoder().encodeToString(hash))
                            .putString(KEY_SALT, Base64.getEncoder().encodeToString(salt))
                            .remove(KEY_LAST_FAIL)
                            .remove(KEY_FAIL_COUNT)
                            .apply();

                    wipeInputs();
                    openAfterUnlock();
                    return;
                }

                if (currentSalt == null) {
                    resetSecurityState(prefs);
		    toast(R.string.security_reset_required);
                    restart();
                    return;
                }

                final long lastFail = prefs.getLong(KEY_LAST_FAIL, 0L);
                if (SystemClock.elapsedRealtime() - lastFail < DELAY_MS) {
		    toast(R.string.too_many_attempts_wait);
                    passwordInput.requestFocus();
                    wipeInputs();
                    return;
                }

                final byte[] salt;
                try {
                    salt = Base64.getDecoder().decode(currentSalt);
                } catch (Exception e) {
                    resetSecurityState(prefs);
		    toast(R.string.security_reset_required);
                    restart();
                    return;
                }

                final byte[] hash = derive(password, salt);
                final String hashB64 = Base64.getEncoder().encodeToString(hash);

                if (safeEquals(currentHash, hashB64)) {

                    prefs.edit()
                            .remove(KEY_LAST_FAIL)
                            .remove(KEY_FAIL_COUNT)
                            .apply();

                    wipeInputs();
                    openAfterUnlock();

                } else {
                    int failCount = prefs.getInt(KEY_FAIL_COUNT, 0) + 1;

                    prefs.edit()
                            .putLong(KEY_LAST_FAIL, SystemClock.elapsedRealtime())
                            .putInt(KEY_FAIL_COUNT, failCount)
                            .apply();

                    if (failCount >= MAX_ATTEMPTS) {
                        performFullWipe(prefs);
			toast(R.string.too_many_attempts_wiped);
                        restart();
                        return;
                    }

		    toast(R.string.incorrect_password);
                    passwordInput.requestFocus();
                    wipeInputs();
                }

            } catch (Exception e) {
		toast(R.string.authentication_error);
                wipeInputs();
            }
        });
    }

    @Override
    protected void onStop() {
        super.onStop();
        // DO NOTHING
    }

    private void performFullWipe(SharedPreferences prefs) {

        SessionManager.lock(); // 🔥 FIX

        try {
            NotesRepository repo = new NotesRepository(this);
            repo.deleteAll();
        } catch (Exception ignored) {}

        try {
            File dbDir = new File(getApplicationInfo().dataDir + "/databases/");
            if (dbDir.exists()) {
                File[] files = dbDir.listFiles();
                if (files != null) {
                    for (File f : files) {
                        if (f != null) f.delete();
                    }
                }
            }
        } catch (Exception ignored) {}

        try {
            deleteRecursive(getCacheDir());
        } catch (Exception ignored) {}

        resetSecurityState(prefs);
    }

    private void deleteRecursive(File file) {
        if (file == null || !file.exists()) return;

        if (file.isDirectory()) {
            File[] children = file.listFiles();
            if (children != null) {
                for (File child : children) {
                    deleteRecursive(child);
                }
            }
        }

        file.delete();
    }

    private void openAfterUnlock() {
        final Intent sourceIntent = getIntent();
        final String target = sourceIntent != null
                ? sourceIntent.getStringExtra("after_unlock")
                : null;

        final Intent intent;

        if (NoteEditorActivity.class.getName().equals(target)) {
            intent = new Intent(this, NoteEditorActivity.class);
        } else {
            intent = new Intent(this, NotesListActivity.class);
        }

        if (sourceIntent != null && sourceIntent.getExtras() != null) {
            Bundle extras = new Bundle(sourceIntent.getExtras());
            extras.remove("after_unlock");
            intent.putExtras(extras);
        }

        SessionManager.unlock();
        startActivity(intent);
        finish();
    }

    private void restart() {
        Intent i = new Intent(this, LockActivity.class);
        i.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_NEW_TASK);
        startActivity(i);
        finish();
    }

    private void wipeInputs() {
        passwordInput.setText("");
        passwordInput.clearFocus();
        if (confirmInput != null) {
            confirmInput.setText("");
            confirmInput.clearFocus();
        }
    }

    private void toast(int stringRes) {
        Toast.makeText(this, stringRes, Toast.LENGTH_SHORT).show();
    }

    private static void resetSecurityState(SharedPreferences prefs) {
        prefs.edit()
                .remove(KEY_HASH)
                .remove(KEY_SALT)
                .remove(KEY_LAST_FAIL)
                .remove(KEY_FAIL_COUNT)
                .apply();
    }

    private static byte[] derive(String password, byte[] salt) throws Exception {
        SecretKeyFactory factory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA256");
        KeySpec spec = new PBEKeySpec(password.toCharArray(), salt, ITERATIONS, KEY_LENGTH);
        return factory.generateSecret(spec).getEncoded();
    }

    private static byte[] generateSalt() {
        byte[] salt = new byte[16];
        new SecureRandom().nextBytes(salt);
        return salt;
    }

    // 🔥 FIXED constant-time compare
    private static boolean safeEquals(String a, String b) {
        if (a == null || b == null) return false;

        byte[] aBytes = a.getBytes();
        byte[] bBytes = b.getBytes();

        if (aBytes.length != bBytes.length) return false;

        int result = 0;
        for (int i = 0; i < aBytes.length; i++) {
            result |= aBytes[i] ^ bBytes[i];
        }

        return result == 0;
    }
}
