package com.secureline.server;

import android.app.KeyguardManager;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.os.Handler;
import android.os.Looper;
import android.os.SystemClock;
import android.util.Slog;

final class SecureLineEnforcementEngine {

    interface Callbacks {
        int getCurrentState();
        void requestStateReevaluation(String reason);
        void onUserPresent(int currentState);
        void onDeviceUnlocked();
        void onDeviceLocked();
        void onSecurityViolation(String reason);
        boolean isTamperLockEnabled();
        int getTamperSensitivity();
    }

    private static final String TAG = "SecureLineEnforce";

    private static final long UNLOCK_VERIFY_INITIAL_DELAY_MS = 100L;
    private static final long UNLOCK_VERIFY_STEP_MS = 100L;
    private static final long UNLOCK_VERIFY_TIMEOUT_MS = 8000L;

    private final Context mContext;
    private final Handler mHandler;

    private final SecureLineScreenLoopController mScreenLoop;
    private final Callbacks mCallbacks;

    private final SecureLineTamperDetector mTamperDetector;

    private int mLastState = SecureLineState.BOOTSTRAP;
    private boolean mRegistered = false;

    /*
     * Guards unlock verification loops so we do not end up with
     * multiple concurrent delayed checks from repeated USER_PRESENT.
     *
     * mUnlockVerificationGeneration is incremented whenever:
     * - a new verification starts
     * - the engine stops
     *
     * Delayed runnables only proceed if their generation still matches.
     */
    private boolean mUnlockVerificationRunning = false;
    private int mUnlockVerificationGeneration = 0;

    SecureLineEnforcementEngine(Context context, Callbacks callbacks) {
        mContext = context;
        mCallbacks = callbacks;
        mHandler = new Handler(Looper.getMainLooper());
        mScreenLoop = new SecureLineScreenLoopController(context);

        mTamperDetector = new SecureLineTamperDetector(
                context,
                () -> mCallbacks.isTamperLockEnabled(),
                () -> mCallbacks.getTamperSensitivity(),
                () -> notifySecurityViolationAsync("tamper_motion")
        );
    }

    void start() {
        if (mRegistered) {
            return;
        }

        IntentFilter f = new IntentFilter();
        f.addAction(Intent.ACTION_USER_PRESENT);
        f.addAction(Intent.ACTION_SCREEN_ON);
        f.addAction(Intent.ACTION_TIME_CHANGED);
        f.addAction(Intent.ACTION_DATE_CHANGED);
        f.addAction(Intent.ACTION_TIMEZONE_CHANGED);

        mContext.registerReceiver(mReceiver, f, Context.RECEIVER_NOT_EXPORTED);
        mRegistered = true;

        mTamperDetector.start();

        final int current = safeGetState();
        mLastState = current;

        Slog.i(TAG, "Enforcement engine started (state=" + current + ")");
        applyState(current);
    }

    void stop() {
        if (!mRegistered) {
            return;
        }

        try {
            mContext.unregisterReceiver(mReceiver);
        } catch (Throwable ignored) {
        }

        mRegistered = false;

        /*
         * Invalidate any pending unlock verification callbacks.
         * They may still be present in the looper queue, but they will
         * self-cancel because the generation no longer matches.
         */
        mUnlockVerificationGeneration++;
        mUnlockVerificationRunning = false;

        mScreenLoop.stop();
        mTamperDetector.stop();

        Slog.i(TAG, "Enforcement engine stopped");
    }

    void onStateChanged(int newState) {
        if (newState == mLastState) {
            return;
        }

        mLastState = newState;
        Slog.i(TAG, "State changed -> " + newState);

        applyState(newState);
    }

    private void applyState(int state) {
        switch (state) {
            case SecureLineState.WAITING_FIRST_UNLOCK:
            case SecureLineState.ACTIVE:
            case SecureLineState.BOOTSTRAP:
            default:
                mScreenLoop.stop();
                break;

            case SecureLineState.MID_LOCK:
                mScreenLoop.stop();
                break;

            case SecureLineState.HARD_LOCK:
                mScreenLoop.startHardLock();
                break;
        }
    }

    private int safeGetState() {
        try {
            return mCallbacks.getCurrentState();
        } catch (Throwable t) {
            Slog.e(TAG, "getCurrentState() failed, default HARD_LOCK", t);
            return SecureLineState.HARD_LOCK;
        }
    }

    private void requestReevalAsync(String reason) {
        mHandler.post(() -> {
            if (!mRegistered) {
                return;
            }

            try {
                mCallbacks.requestStateReevaluation(reason);
            } catch (Throwable t) {
                Slog.e(TAG, "requestStateReevaluation failed: " + reason, t);
            }
        });
    }

    private void notifyUserPresentAsync(int state) {
        mHandler.post(() -> {
            if (!mRegistered) {
                return;
            }

            try {
                mCallbacks.onUserPresent(state);
            } catch (Throwable t) {
                Slog.e(TAG, "onUserPresent failed", t);
            }
        });
    }

    private void notifyDeviceUnlockedAsync() {
        mHandler.post(() -> {
            if (!mRegistered) {
                return;
            }

            try {
                mCallbacks.onDeviceUnlocked();
            } catch (Throwable t) {
                Slog.e(TAG, "onDeviceUnlocked failed", t);
            }
        });
    }

    private void notifySecurityViolationAsync(String reason) {
        mHandler.post(() -> {
            if (!mRegistered) {
                return;
            }

            try {
                Slog.e(TAG, "SECURITY VIOLATION: " + reason);
                mCallbacks.onSecurityViolation(reason);
            } catch (Throwable t) {
                Slog.e(TAG, "onSecurityViolation failed", t);
            }
        });
    }

    private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
        @Override
        public void onReceive(Context context, Intent intent) {
            final String a = (intent != null) ? intent.getAction() : null;
            if (a == null) {
                return;
            }

            switch (a) {
		case Intent.ACTION_USER_PRESENT:

		    /*
		     USER_PRESENT bruges kun til UI events
		     må IKKE trigge unlock direkte
		    */

		    notifyUserPresentAsync(safeGetState());

		    break;

                case Intent.ACTION_SCREEN_ON:
                    break;

                case Intent.ACTION_TIME_CHANGED:
                case Intent.ACTION_DATE_CHANGED:
                case Intent.ACTION_TIMEZONE_CHANGED:
                    requestReevalAsync("time_changed");
                    break;

                default:
                    break;
            }
        }
    };
}
